suppressPackageStartupMessages(library(igraph))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(tidyr))
suppressPackageStartupMessages(library(data.table))
suppressPackageStartupMessages(source("CCLasso.R"))
set.seed(19825791)
# coefficient of variation
co.var <- function(x, na.rm = TRUE) {
100 * (sd(x, na.rm = na.rm) / mean(x, na.rm = na.rm))
}
# kjhealy/polar-labels.r on github:
# https://gist.github.com/kjhealy/834774#file-polar-labels-r
radian.rescale <- function(x,
start = 0,
direction = 1) {
c.rotate <- function(x) (x + start) %% (2 * pi) * direction
c.rotate(scales::rescale(x, c(0, 2 * pi), range(x)))
}
merge.easy <- function(df1, df2, key) {
df1 <- data.table(df1, key = key)
df2 <- data.table(df2, key = key)
return(as.data.frame(unique(
merge(
df1,
df2,
all.x = TRUE,
by = .EACHI,
allow.cartesian = TRUE
)
), stringsAsFactors = FALSE))
}
# quartile coefficient of dispersion
qcd <- function(x) {
(quantile(x, 0.75) - quantile(x, 0.25)) /
(quantile(x, 0.75) + quantile(x, 0.25))
}
plot_network <- function(ig, coord = layout_with_fr(ig), ...) {
plot(
ig,
layout = coord,
vertex.size = 2,
vertex.label = V(ig)$name,
vertex.label.dist = 1,
vertex.label.cex = 0.25,
vertex.label.degree = radian.rescale(
x = 1:vcount(ig),
direction = -1,
start = 0
),
edge.color = ifelse(E(ig)$weight > 0, 'green', 'red'),
...
)
}
Variance and Coefficient of Variation Summary Statistics
Call:
lm(formula = log(apply(pfam_data, 2, var)) ~ log(apply(pfam_data,
2, mean, na.rm = TRUE)))
Residuals:
Min 1Q Median 3Q Max
-2.6631 -0.4140 0.0129 0.3877 3.6636
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.151887 0.016713 188.6 <2e-16 ***
log(apply(pfam_data, 2, mean, na.rm = TRUE)) 1.095131 0.004401 248.8 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.7279 on 6091 degrees of freedom
Multiple R-squared: 0.9104, Adjusted R-squared: 0.9104
F-statistic: 6.191e+04 on 1 and 6091 DF, p-value: < 2.2e-16
Call:
lm(formula = log(apply(pfam_data, 2, co.var)) ~ log(apply(pfam_data,
2, mean, na.rm = TRUE)))
Residuals:
Min 1Q Median 3Q Max
-1.33154 -0.20699 0.00644 0.19387 1.83182
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.181113 0.008357 739.7 <2e-16 ***
log(apply(pfam_data, 2, mean, na.rm = TRUE)) -0.452435 0.002201 -205.6 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3639 on 6091 degrees of freedom
Multiple R-squared: 0.874, Adjusted R-squared: 0.874
F-statistic: 4.227e+04 on 1 and 6091 DF, p-value: < 2.2e-16
par(mfrow = c(1, 2), mar = c(5.1, 4.1, 4.1, 2.1))
plot(
apply(pfam_data, 2, mean, na.rm = TRUE),
apply(pfam_data, 2, var),
log = "xy",
xlab = "Mean",
ylab = "Variance"
)
plot(
apply(pfam_data, 2, mean, na.rm = TRUE),
apply(pfam_data, 2, co.var),
log = "xy",
xlab = "Mean",
ylab = "Coefficient of Variation"
)

par(mfrow = c(1, 2), mar = c(5.1, 4.1, 4.1, 2.1))
# hist(apply(pfam_data, 2, mean, na.rm = TRUE))
hist(
apply(pfam_data, 2, co.var),
breaks = 100,
xlab = "Coefficient of Variation",
main = "Coefficient of Variation Histogram"
)
plot(density(apply(pfam_data, 2, co.var)),
main = "Coefficient of Variation Density")

Remove features with low coefficient of variation:

Build Network
# CCLasso takes many hours to run (load cached if available)
ccl_pfam_file <- "data/ccl_pfam_cv200.Rda"
if (!file.exists(ccl_pfam_file)) {
ccl_pfam <- cclasso(as.matrix(pfam_data), counts = TRUE)
save(ccl_pfam, file = ccl_pfam_file)
} else {
load(ccl_pfam_file)
}
ccl_pfam$cor_w[is.nan(ccl_pfam$cor_w)] <- 0
ccl_pfam_pvals_vec <- ccl_pfam$p_vals[upper.tri(ccl_pfam$p_vals)]
ccl_pfam_pvals_adj <- p.adjust(ccl_pfam_pvals_vec, "BH")
ccl_pfam_edges <- ccl_pfam$cor_w[upper.tri(ccl_pfam$cor_w)]
# p-value < 1e-4 otherwise CCLasso network number of edges is very large
ccl_pfam_edges[ccl_pfam_pvals_adj > 1e-4] <- 0
ccl_pfam_amat <- matrix(0, dim(pfam_data)[2], dim(pfam_data)[2])
rownames(ccl_pfam_amat) <- colnames(pfam_data)
colnames(ccl_pfam_amat) <- colnames(pfam_data)
ccl_pfam_amat[upper.tri(ccl_pfam_amat)] <- ccl_pfam_edges
ccl_pfam_g <- graph_from_adjacency_matrix(
ccl_pfam_amat,
mode = "upper",
weighted = TRUE,
diag = FALSE
)
ccl_pfam_g <- induced_subgraph(
ccl_pfam_g,
V(ccl_pfam_g)[
components(ccl_pfam_g)$membership ==
which.max(components(ccl_pfam_g)$csize)
]
)
ccl_pfam_p_g <- delete_edges(
ccl_pfam_g,
E(ccl_pfam_g)[E(ccl_pfam_g)$weight < 0]
)
ccl_pfam_p_g <- induced_subgraph(
ccl_pfam_p_g,
V(ccl_pfam_p_g)[
components(ccl_pfam_p_g)$membership ==
which.max(components(ccl_pfam_p_g)$csize)
]
)
ccl_pfam_p_g_v_msk <- (
V(ccl_pfam_g)$name %in% V(ccl_pfam_p_g)$name
)
ccl_pfam_n_g <- delete_edges(
ccl_pfam_g,
E(ccl_pfam_g)[E(ccl_pfam_g)$weight > 0]
)
ccl_pfam_n_g <- induced_subgraph(
ccl_pfam_n_g,
V(ccl_pfam_n_g)[
components(ccl_pfam_n_g)$membership ==
which.max(components(ccl_pfam_n_g)$csize)
]
)
ccl_pfam_n_g_v_msk <- (
V(ccl_pfam_g)$name %in% V(ccl_pfam_n_g)$name
)
Visualize Network
ccl_pfam_g_coord <- layout_with_lgl(ccl_pfam_g)
ccl_pfam_p_g_coord <- ccl_pfam_g_coord[ccl_pfam_p_g_v_msk, , drop = F]
ccl_pfam_n_g_coord <- ccl_pfam_g_coord[ccl_pfam_n_g_v_msk, , drop = F]
par(mfrow = c(1, 3), mar = c(1, 1, 3, 1))
plot_network(ccl_pfam_g,
coord = ccl_pfam_g_coord,
main = "PFAM CCLasso Network")
plot_network(ccl_pfam_p_g,
coord = ccl_pfam_p_g_coord,
main = "+ Interactions")

Network Statistics
Degree distribution, shortest paths distance distribution, transitivity:
par(mfrow = c(2, 2), mar = c(5.1, 4.1, 4.1, 2.1))
# degree distribution
k <- degree(ccl_pfam_g)
hist(k,
breaks = 100,
xlab = "k",
ylab = "Frequency",
main = "Degree Histogram")
dd <- degree_distribution(ccl_pfam_g)
dd_nzero_pos <- which(dd != 0)
pk <- dd[dd_nzero_pos]
dx <- 1:max(k)
dx <- dx[dd_nzero_pos]
plot(
pk ~ log(dx),
log = "xy",
xlab = "log k",
ylab = "log Pk",
main = "Log-Log Degree Distribution"
)
# distance distribution of shortest paths
nv <- vcount(ccl_pfam_g)
bfs_vec <- matrix(nrow = nv, ncol = nv)
for (i in 1:nv) {
bfs_vec[i, ] <- bfs(
ccl_pfam_g,
root = i,
dist = TRUE,
unreachable = FALSE
)$dist
}
distd <- table(bfs_vec) / sum(bfs_vec, na.rm = TRUE)
distd <- tail(distd, length(distd) - 1)
plot(
names(distd),
distd,
xlab = "Distance",
ylab = expression(P[Distance]),
main = "Distance Distribution of Shortest Paths"
)
# clustering
cl <- transitivity(ccl_pfam_g, type = "local")
cl_df <- data.frame(clust = cl, degree = k) %>%
group_by(degree) %>%
summarize(mclust = mean(clust, na.rm = TRUE))
plot(
mclust ~ degree,
data = cl_df,
xlab = "k",
ylab = "C(k)",
main = "Clustering Coefficent"
)

Community Detection in Networks Using InfoMap and SpinGlass
num_top_clusts <- 3
# InfoMap +Network
ccl_pfam_p_g_imap <- cluster_infomap(ccl_pfam_p_g)
ccl_pfam_p_g_imap_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_p_g_imap), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_p_g_imap_top_v_msk <- (
ccl_pfam_p_g_imap$membership %in% ccl_pfam_p_g_imap_top_ids
)
ccl_pfam_p_g_imap_top_g <- induced_subgraph(
ccl_pfam_p_g,
V(ccl_pfam_p_g)[ccl_pfam_p_g_imap_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_p_g_imap_top <- ccl_pfam_p_g_imap
ccl_pfam_p_g_imap_top$names <-
ccl_pfam_p_g_imap_top$names[ccl_pfam_p_g_imap_top_v_msk]
ccl_pfam_p_g_imap_top$membership <-
ccl_pfam_p_g_imap_top$membership[ccl_pfam_p_g_imap_top_v_msk]
ccl_pfam_p_g_imap_top$vcount <- length(ccl_pfam_p_g_imap_top$names)
# SpinGlass +Network
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_p_g_spin_file <- "data/ccl_pfam_p_g_spin.Rda"
if (!file.exists(ccl_pfam_p_g_spin_file)) {
ccl_pfam_p_g_spin <- cluster_spinglass(ccl_pfam_p_g)
save(ccl_pfam_p_g_spin, file = ccl_pfam_p_g_spin_file)
} else {
load(ccl_pfam_p_g_spin_file)
}
ccl_pfam_p_g_spin_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_p_g_spin), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_p_g_spin_top_v_msk <- (
ccl_pfam_p_g_spin$membership %in% ccl_pfam_p_g_spin_top_ids
)
ccl_pfam_p_g_spin_top_g <- induced_subgraph(
ccl_pfam_p_g,
V(ccl_pfam_p_g)[ccl_pfam_p_g_spin_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_p_g_spin_top <- ccl_pfam_p_g_spin
ccl_pfam_p_g_spin_top$names <-
ccl_pfam_p_g_spin_top$names[ccl_pfam_p_g_spin_top_v_msk]
ccl_pfam_p_g_spin_top$membership <-
ccl_pfam_p_g_spin_top$membership[ccl_pfam_p_g_spin_top_v_msk]
ccl_pfam_p_g_spin_top$vcount <- length(ccl_pfam_p_g_spin_top$names)
# SpinGlass +/-Network
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_g_spin_file <- "data/ccl_pfam_g_spin.Rda"
if (!file.exists(ccl_pfam_g_spin_file)) {
ccl_pfam_g_spin <- cluster_spinglass(ccl_pfam_g,
implementation = "neg")
save(ccl_pfam_g_spin, file = ccl_pfam_g_spin_file)
} else {
load(ccl_pfam_g_spin_file)
}
ccl_pfam_g_spin_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_g_spin), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_g_spin_top_v_msk <- (
ccl_pfam_g_spin$membership %in% ccl_pfam_g_spin_top_ids
)
ccl_pfam_g_spin_top_g <- induced_subgraph(
ccl_pfam_g,
V(ccl_pfam_g)[ccl_pfam_g_spin_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_g_spin_top <- ccl_pfam_g_spin
ccl_pfam_g_spin_top$names <-
ccl_pfam_g_spin_top$names[ccl_pfam_g_spin_top_v_msk]
ccl_pfam_g_spin_top$membership <-
ccl_pfam_g_spin_top$membership[ccl_pfam_g_spin_top_v_msk]
ccl_pfam_g_spin_top$vcount <- length(ccl_pfam_g_spin_top$names)
par(mfrow = c(3, 2), mar = c(1, 1, 3, 1))
plot(
ccl_pfam_p_g_imap,
ccl_pfam_p_g,
layout = ccl_pfam_p_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM CCLasso +Network InfoMap Clusters"
)
plot(
ccl_pfam_p_g_imap_top,
ccl_pfam_p_g_imap_top_g,
col = membership(ccl_pfam_p_g_imap)[ccl_pfam_p_g_imap_top_v_msk],
layout = ccl_pfam_p_g_coord[ccl_pfam_p_g_imap_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(ccl_pfam_p_g_imap)),
alpha = 1)[ccl_pfam_p_g_imap_top_ids],
mark.col = rainbow(length(communities(ccl_pfam_p_g_imap)),
alpha = 0.3)[ccl_pfam_p_g_imap_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_p_g_spin,
ccl_pfam_p_g,
layout = ccl_pfam_p_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM CCLasso +Network SpinGlass Clusters"
)
plot(
ccl_pfam_p_g_spin_top,
ccl_pfam_p_g_spin_top_g,
col = membership(ccl_pfam_p_g_spin)[ccl_pfam_p_g_spin_top_v_msk],
layout = ccl_pfam_p_g_coord[ccl_pfam_p_g_spin_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(ccl_pfam_p_g_spin)),
alpha = 1)[ccl_pfam_p_g_spin_top_ids],
mark.col = rainbow(length(communities(ccl_pfam_p_g_spin)),
alpha = 0.3)[ccl_pfam_p_g_spin_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_g_spin,
ccl_pfam_g,
layout = ccl_pfam_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM CCLasso +/-Network SpinGlass Clusters"
)
plot(
ccl_pfam_g_spin_top,
ccl_pfam_g_spin_top_g,
col = membership(ccl_pfam_g_spin)[ccl_pfam_g_spin_top_v_msk],
layout = ccl_pfam_g_coord[ccl_pfam_g_spin_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(ccl_pfam_g_spin)),
alpha = 1)[ccl_pfam_g_spin_top_ids],
mark.col = rainbow(length(communities(ccl_pfam_g_spin)),
alpha = 0.3)[ccl_pfam_g_spin_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)

Save cluster results:
ccl_pfam_p_g_imap_data <- data.frame()
for (i in 1:max(ccl_pfam_p_g_imap$membership)) {
ccl_pfam_p_g_imap_data <- rbind(
ccl_pfam_p_g_imap_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_p_g)$name[
ccl_pfam_p_g_imap$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_p_g_imap_data <-
merge.easy(ccl_pfam_p_g_imap_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_p_g_imap_data,
file = "results/Pfam_CCLassoPosNet_InfoMapClusters.csv",
row.names = FALSE)
ccl_pfam_p_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_p_g_spin$membership)) {
ccl_pfam_p_g_spin_data <- rbind(
ccl_pfam_p_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_p_g)$name[
ccl_pfam_p_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_p_g_spin_data <-
merge.easy(ccl_pfam_p_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_p_g_spin_data,
file = "results/Pfam_CCLassoPosNet_SpinGlassClusters.csv",
row.names = FALSE)
ccl_pfam_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_g_spin$membership)) {
ccl_pfam_g_spin_data <- rbind(
ccl_pfam_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_g)$name[
ccl_pfam_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_g_spin_data <-
merge.easy(ccl_pfam_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_g_spin_data,
file = "results/Pfam_CCLassoNet_SpinGlassClusters.csv",
row.names = FALSE)
Build Network Including Patient Response
# Add patient response to dataset
pfam_resp_data <- pfam_data
pfam_resp_data$Sample <- rownames(pfam_resp_data)
pfam_resp_data <- merge.easy(pfam_resp_data, patient_meta, key = "Sample")
rownames(pfam_resp_data) <- pfam_resp_data$Sample
pfam_resp_data$Sample <- NULL
# CCLasso takes many hours to run (load cached if available)
ccl_pfam_resp_file <- "data/ccl_pfam_resp_cv200.Rda"
if (!file.exists(ccl_pfam_resp_file)) {
ccl_pfam_resp <- cclasso(as.matrix(pfam_resp_data), counts = TRUE)
save(ccl_pfam_resp, file = ccl_pfam_resp_file)
} else {
load(ccl_pfam_resp_file)
}
ccl_pfam_resp$cor_w[is.nan(ccl_pfam_resp$cor_w)] <- 0
ccl_pfam_resp_pvals_vec <- ccl_pfam_resp$p_vals[upper.tri(ccl_pfam_resp$p_vals)]
ccl_pfam_resp_pvals_adj <- p.adjust(ccl_pfam_resp_pvals_vec, "BH")
ccl_pfam_resp_edges <- ccl_pfam_resp$cor_w[upper.tri(ccl_pfam_resp$cor_w)]
# p-value < 1e-4 otherwise CCLasso network number of edges is very large
ccl_pfam_resp_edges[ccl_pfam_resp_pvals_adj > 1e-4] <- 0
ccl_pfam_resp_amat <- matrix(0, dim(pfam_resp_data)[2], dim(pfam_resp_data)[2])
rownames(ccl_pfam_resp_amat) <- colnames(pfam_resp_data)
colnames(ccl_pfam_resp_amat) <- colnames(pfam_resp_data)
ccl_pfam_resp_amat[upper.tri(ccl_pfam_resp_amat)] <- ccl_pfam_resp_edges
ccl_pfam_resp_g <- graph_from_adjacency_matrix(
ccl_pfam_resp_amat,
mode = "upper",
weighted = TRUE,
diag = FALSE
)
ccl_pfam_resp_g <- induced_subgraph(
ccl_pfam_resp_g,
V(ccl_pfam_resp_g)[
components(ccl_pfam_resp_g)$membership ==
which.max(components(ccl_pfam_resp_g)$csize)
]
)
ccl_pfam_resp_p_g <- delete_edges(
ccl_pfam_resp_g,
E(ccl_pfam_resp_g)[E(ccl_pfam_resp_g)$weight < 0]
)
ccl_pfam_resp_p_g <- induced_subgraph(
ccl_pfam_resp_p_g,
V(ccl_pfam_resp_p_g)[
components(ccl_pfam_resp_p_g)$membership ==
which.max(components(ccl_pfam_resp_p_g)$csize)
]
)
ccl_pfam_resp_p_g_v_msk <- (
V(ccl_pfam_resp_g)$name %in% V(ccl_pfam_resp_p_g)$name
)
ccl_pfam_resp_n_g <- delete_edges(
ccl_pfam_resp_g,
E(ccl_pfam_resp_g)[E(ccl_pfam_resp_g)$weight > 0]
)
ccl_pfam_resp_n_g <- induced_subgraph(
ccl_pfam_resp_n_g,
V(ccl_pfam_resp_n_g)[
components(ccl_pfam_resp_n_g)$membership ==
which.max(components(ccl_pfam_resp_n_g)$csize)
]
)
ccl_pfam_resp_n_g_v_msk <- (
V(ccl_pfam_resp_g)$name %in% V(ccl_pfam_resp_n_g)$name
)
ccl_pfam_resp_g_coord <- layout_with_lgl(ccl_pfam_resp_g)
ccl_pfam_resp_p_g_coord <-
ccl_pfam_resp_g_coord[ccl_pfam_resp_p_g_v_msk, , drop = F]
ccl_pfam_resp_n_g_coord <-
ccl_pfam_resp_g_coord[ccl_pfam_resp_n_g_v_msk, , drop = F]
Where does response cluster?
ccl_pfam_resp_p_g_imap <- cluster_infomap(ccl_pfam_resp_p_g)
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_resp_p_g_spin_file <- "data/ccl_pfam_resp_p_g_spin.Rda"
if (!file.exists(ccl_pfam_resp_p_g_spin_file)) {
ccl_pfam_resp_p_g_spin <- cluster_spinglass(ccl_pfam_resp_p_g)
save(ccl_pfam_resp_p_g_spin, file = ccl_pfam_resp_p_g_spin_file)
} else {
load(ccl_pfam_resp_p_g_spin_file)
}
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_resp_g_spin_file <- "data/ccl_pfam_resp_g_spin.Rda"
if (!file.exists(ccl_pfam_resp_g_spin_file)) {
ccl_pfam_resp_g_spin <- cluster_spinglass(ccl_pfam_resp_g,
implementation = "neg")
save(ccl_pfam_resp_g_spin, file = ccl_pfam_resp_g_spin_file)
} else {
load(ccl_pfam_resp_g_spin_file)
}
ccl_pfam_resp_p_g_imap_data <- data.frame()
for (i in 1:max(ccl_pfam_resp_p_g_imap$membership)) {
ccl_pfam_resp_p_g_imap_data <- rbind(
ccl_pfam_resp_p_g_imap_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_resp_p_g)$name[
ccl_pfam_resp_p_g_imap$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_resp_p_g_imap_data <-
merge.easy(ccl_pfam_resp_p_g_imap_data, pfam_feat, key = "Accession")
ccl_pfam_resp_p_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_resp_p_g_spin$membership)) {
ccl_pfam_resp_p_g_spin_data <- rbind(
ccl_pfam_resp_p_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_resp_p_g)$name[
ccl_pfam_resp_p_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_resp_p_g_spin_data <-
merge.easy(ccl_pfam_resp_p_g_spin_data, pfam_feat, key = "Accession")
ccl_pfam_resp_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_resp_g_spin$membership)) {
ccl_pfam_resp_g_spin_data <- rbind(
ccl_pfam_resp_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_resp_g)$name[
ccl_pfam_resp_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_resp_g_spin_data <-
merge.easy(ccl_pfam_resp_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_resp_p_g_imap_data,
file = "results/PfamRes_CCLassoPosNet_InfoMapClusters.csv",
row.names = FALSE)
write.csv(ccl_pfam_resp_p_g_spin_data,
file = "results/PfamRes_CCLassoPosNet_SpinGlassClusters.csv",
row.names = FALSE)
write.csv(ccl_pfam_resp_g_spin_data,
file = "results/PfamRes_CCLassoNet_SpinGlassClusters.csv",
row.names = FALSE)
cat(
"InfoMap +Network response cluster:",
ccl_pfam_resp_p_g_imap_data$Cluster[
ccl_pfam_resp_p_g_imap_data$Accession == "ResponseBinary"
],
"\nSpinGlass +Network response cluster:",
ccl_pfam_resp_p_g_spin_data$Cluster[
ccl_pfam_resp_p_g_spin_data$Accession == "ResponseBinary"
],
"\nSpinGlass +/-Network response cluster:",
ccl_pfam_resp_g_spin_data$Cluster[
ccl_pfam_resp_g_spin_data$Accession == "ResponseBinary"
],
"\n"
)
InfoMap +Network response cluster: 2
SpinGlass +Network response cluster: 4
SpinGlass +/-Network response cluster: 1
Bipartite Graphs of InfoMap and SpinGlass Clusters Effect on Response
ccl_pfam_p_g_imap_clusts <- data.frame(
Node = V(ccl_pfam_p_g)$name,
Cluster = ccl_pfam_p_g_imap$membership,
stringsAsFactors = FALSE
)
ccl_pfam_p_g_spin_clusts <- data.frame(
Node = V(ccl_pfam_p_g)$name,
Cluster = ccl_pfam_p_g_spin$membership,
stringsAsFactors = FALSE
)
ccl_pfam_g_spin_clusts <- data.frame(
Node = V(ccl_pfam_g)$name,
Cluster = ccl_pfam_g_spin$membership,
stringsAsFactors = FALSE
)
ccl_pfam_resp_g_edges <- cbind(as_edgelist(ccl_pfam_resp_g),
E(ccl_pfam_resp_g)$weight)
ccl_pfam_resp_g_resp_edges <-
as.data.frame(ccl_pfam_resp_g_edges[c(
ccl_pfam_resp_g_edges[, 1] == "ResponseBinary" |
ccl_pfam_resp_g_edges[, 2] == "ResponseBinary"
), ], stringsAsFactors = FALSE)
names(ccl_pfam_resp_g_resp_edges) <- c("Node", "Site", "Weight")
ccl_pfam_resp_p_g_imap_clusts_resp_edges <-
merge.easy(ccl_pfam_resp_g_resp_edges,
ccl_pfam_p_g_imap_clusts,
key = "Node")
ccl_pfam_resp_p_g_imap_clusts_resp_edges$Weight <-
as.numeric(ccl_pfam_resp_p_g_imap_clusts_resp_edges$Weight)
ccl_pfam_resp_p_g_spin_clusts_resp_edges <-
merge.easy(ccl_pfam_resp_g_resp_edges,
ccl_pfam_p_g_spin_clusts,
key = "Node")
ccl_pfam_resp_p_g_spin_clusts_resp_edges$Weight <-
as.numeric(ccl_pfam_resp_p_g_spin_clusts_resp_edges$Weight)
ccl_pfam_resp_g_spin_clusts_resp_edges <-
merge.easy(ccl_pfam_resp_g_resp_edges,
ccl_pfam_g_spin_clusts,
key = "Node")
ccl_pfam_resp_g_spin_clusts_resp_edges$Weight <-
as.numeric(ccl_pfam_resp_g_spin_clusts_resp_edges$Weight)
ccl_pfam_resp_p_g_imap_clusts_resp_clusts <-
ccl_pfam_resp_p_g_imap_clusts_resp_edges %>%
group_by(Site, Cluster) %>%
summarise(Weight = mean(Weight)) %>%
subset(!is.na(Cluster))
ccl_pfam_resp_p_g_spin_clusts_resp_clusts <-
ccl_pfam_resp_p_g_spin_clusts_resp_edges %>%
group_by(Site, Cluster) %>%
summarise(Weight = mean(Weight)) %>%
subset(!is.na(Cluster))
ccl_pfam_resp_g_spin_clusts_resp_clusts <-
ccl_pfam_resp_g_spin_clusts_resp_edges %>%
group_by(Site, Cluster) %>%
summarise(Weight = mean(Weight)) %>%
subset(!is.na(Cluster))
ccl_pfam_resp_p_g_imap_bipart_g <-
graph.data.frame(ccl_pfam_resp_p_g_imap_clusts_resp_clusts[, 1:2],
directed = FALSE)
V(ccl_pfam_resp_p_g_imap_bipart_g)$type <-
V(ccl_pfam_resp_p_g_imap_bipart_g)$name %in%
as.character(unlist(ccl_pfam_resp_p_g_imap_clusts_resp_clusts[, 1]))
E(ccl_pfam_resp_p_g_imap_bipart_g)$weight <-
ccl_pfam_resp_p_g_imap_clusts_resp_clusts$Weight
V(ccl_pfam_resp_p_g_imap_bipart_g)$color <-
V(ccl_pfam_resp_p_g_imap_bipart_g)$type
V(ccl_pfam_resp_p_g_imap_bipart_g)$color <-
gsub("FALSE", rgb(0, 1, 1, 0.2),
V(ccl_pfam_resp_p_g_imap_bipart_g)$color)
V(ccl_pfam_resp_p_g_imap_bipart_g)$color <-
gsub("TRUE", rgb(1, 1, 0, 0.2),
V(ccl_pfam_resp_p_g_imap_bipart_g)$color)
ccl_pfam_resp_p_g_spin_bipart_g <-
graph.data.frame(ccl_pfam_resp_p_g_spin_clusts_resp_clusts[, 1:2],
directed = FALSE)
V(ccl_pfam_resp_p_g_spin_bipart_g)$type <-
V(ccl_pfam_resp_p_g_spin_bipart_g)$name %in%
as.character(unlist(ccl_pfam_resp_p_g_spin_clusts_resp_clusts[, 1]))
E(ccl_pfam_resp_p_g_spin_bipart_g)$weight <-
ccl_pfam_resp_p_g_spin_clusts_resp_clusts$Weight
V(ccl_pfam_resp_p_g_spin_bipart_g)$color <-
V(ccl_pfam_resp_p_g_spin_bipart_g)$type
V(ccl_pfam_resp_p_g_spin_bipart_g)$color <-
gsub("FALSE", rgb(0, 1, 1, 0.2),
V(ccl_pfam_resp_p_g_spin_bipart_g)$color)
V(ccl_pfam_resp_p_g_spin_bipart_g)$color <-
gsub("TRUE", rgb(1, 1, 0, 0.2),
V(ccl_pfam_resp_p_g_spin_bipart_g)$color)
ccl_pfam_resp_g_spin_bipart_g <-
graph.data.frame(ccl_pfam_resp_g_spin_clusts_resp_clusts[, 1:2],
directed = FALSE)
V(ccl_pfam_resp_g_spin_bipart_g)$type <-
V(ccl_pfam_resp_g_spin_bipart_g)$name %in%
as.character(unlist(ccl_pfam_resp_g_spin_clusts_resp_clusts[, 1]))
E(ccl_pfam_resp_g_spin_bipart_g)$weight <-
ccl_pfam_resp_g_spin_clusts_resp_clusts$Weight
V(ccl_pfam_resp_g_spin_bipart_g)$color <-
V(ccl_pfam_resp_g_spin_bipart_g)$type
V(ccl_pfam_resp_g_spin_bipart_g)$color <-
gsub("FALSE", rgb(0, 1, 1, 0.2),
V(ccl_pfam_resp_g_spin_bipart_g)$color)
V(ccl_pfam_resp_g_spin_bipart_g)$color <-
gsub("TRUE", rgb(1, 1, 0, 0.2),
V(ccl_pfam_resp_g_spin_bipart_g)$color)
par(mfrow = c(1, 3), mar = c(1, 1, 3, 1))
plot(
ccl_pfam_resp_p_g_imap_bipart_g,
edge.color = ifelse(
E(ccl_pfam_resp_p_g_imap_bipart_g)$weight > 0,
rgb(0, 1, 0),
rgb(1, 0, 0)
),
edge.width = abs(E(ccl_pfam_resp_p_g_imap_bipart_g)$weight) * 30,
layout = layout_as_bipartite,
vertex.size = 15,
vertex.label.cex = 1,
vertex.label.color = "black",
main = "PFAM Response CCLasso +Network\nInfoMap Cluster Effect on Response"
)
plot(
ccl_pfam_resp_p_g_spin_bipart_g,
edge.color = ifelse(
E(ccl_pfam_resp_p_g_spin_bipart_g)$weight > 0,
rgb(0, 1, 0),
rgb(1, 0, 0)
),
edge.width = abs(E(ccl_pfam_resp_p_g_spin_bipart_g)$weight) * 30,
layout = layout_as_bipartite,
vertex.size = 15,
vertex.label.cex = 1,
vertex.label.color = "black",
main = "PFAM Response CCLasso +Network\nSpinGlass Cluster Effect on Response"
)
plot(
ccl_pfam_resp_g_spin_bipart_g,
edge.color = ifelse(
E(ccl_pfam_resp_g_spin_bipart_g)$weight > 0,
rgb(0, 1, 0),
rgb(1, 0, 0)
),
edge.width = abs(E(ccl_pfam_resp_g_spin_bipart_g)$weight) * 30,
layout = layout_as_bipartite,
vertex.size = 15,
vertex.label.cex = 1,
vertex.label.color = "black",
main = "PFAM Response CCLasso +/-Network\nSpinGlass Cluster Effect on Response"
)

Save cluster bipartite results:
pfam_feat_2 <- pfam_feat
names(pfam_feat_2)[1] <- "Node"
ccl_pfam_p_g_imap_clusts <-
merge.easy(ccl_pfam_p_g_imap_clusts, pfam_feat_2, key = "Node")
ccl_pfam_resp_p_g_imap_clusts_resp_clust_weight <-
ccl_pfam_resp_p_g_imap_clusts_resp_clusts[, c("Cluster", "Weight")]
names(ccl_pfam_resp_p_g_imap_clusts_resp_clust_weight)[2] <- "ResponseEffect"
ccl_pfam_p_g_imap_clusts <-
merge.easy(
ccl_pfam_p_g_imap_clusts,
ccl_pfam_resp_p_g_imap_clusts_resp_clust_weight,
key = "Cluster"
)
write.csv(
ccl_pfam_p_g_imap_clusts,
file = "results/Pfam_CCLassoPosNet_InfoMapClusters_ResponseEffect.csv",
row.names = FALSE
)
ccl_pfam_p_g_spin_clusts <-
merge.easy(ccl_pfam_p_g_spin_clusts, pfam_feat_2, key = "Node")
ccl_pfam_resp_p_g_spin_clusts_resp_clust_weight <-
ccl_pfam_resp_p_g_spin_clusts_resp_clusts[, c("Cluster", "Weight")]
names(ccl_pfam_resp_p_g_spin_clusts_resp_clust_weight)[2] <- "ResponseEffect"
ccl_pfam_p_g_spin_clusts <-
merge.easy(
ccl_pfam_p_g_spin_clusts,
ccl_pfam_resp_p_g_spin_clusts_resp_clust_weight,
key = "Cluster"
)
write.csv(
ccl_pfam_p_g_spin_clusts,
file = "results/Pfam_CCLassoPosNet_SpinGlassClusters_ResponseEffect.csv",
row.names = FALSE
)
ccl_pfam_g_spin_clusts <-
merge.easy(ccl_pfam_g_spin_clusts, pfam_feat_2, key = "Node")
ccl_pfam_resp_g_spin_clusts_resp_clust_weight <-
ccl_pfam_resp_g_spin_clusts_resp_clusts[, c("Cluster", "Weight")]
names(ccl_pfam_resp_g_spin_clusts_resp_clust_weight)[2] <- "ResponseEffect"
ccl_pfam_g_spin_clusts <-
merge.easy(
ccl_pfam_g_spin_clusts,
ccl_pfam_resp_g_spin_clusts_resp_clust_weight,
key = "Cluster"
)
write.csv(
ccl_pfam_g_spin_clusts,
file = "results/Pfam_CCLassoNet_SpinGlassClusters_ResponseEffect.csv",
row.names = FALSE
)
Build Responder and Non-Responder Networks
# Responder
pfam_rspdr_data <-
pfam_data[patient_meta$Sample[patient_meta$ResponseBinary == 1], ]
# CCLasso takes many hours to run (load cached if available)
ccl_pfam_rspdr_file <- "data/ccl_pfam_rspdr_cv200.Rda"
if (!file.exists(ccl_pfam_rspdr_file)) {
ccl_pfam_rspdr <- cclasso(as.matrix(pfam_rspdr_data), counts = TRUE)
save(ccl_pfam_rspdr, file = ccl_pfam_rspdr_file)
} else {
load(ccl_pfam_rspdr_file)
}
ccl_pfam_rspdr$cor_w[is.nan(ccl_pfam_rspdr$cor_w)] <- 0
ccl_pfam_rspdr_pvals_vec <-
ccl_pfam_rspdr$p_vals[upper.tri(ccl_pfam_rspdr$p_vals)]
ccl_pfam_rspdr_pvals_adj <-
p.adjust(ccl_pfam_rspdr_pvals_vec, "BH")
ccl_pfam_rspdr_edges <-
ccl_pfam_rspdr$cor_w[upper.tri(ccl_pfam_rspdr$cor_w)]
# p-value < 1e-4 otherwise CCLasso network number of edges is very large
ccl_pfam_rspdr_edges[ccl_pfam_rspdr_pvals_adj > 1e-4] <- 0
ccl_pfam_rspdr_amat <-
matrix(0, dim(pfam_rspdr_data)[2], dim(pfam_rspdr_data)[2])
rownames(ccl_pfam_rspdr_amat) <- colnames(pfam_rspdr_data)
colnames(ccl_pfam_rspdr_amat) <- colnames(pfam_rspdr_data)
ccl_pfam_rspdr_amat[upper.tri(ccl_pfam_rspdr_amat)] <- ccl_pfam_rspdr_edges
ccl_pfam_rspdr_g <- graph_from_adjacency_matrix(
ccl_pfam_rspdr_amat,
mode = "upper",
weighted = TRUE,
diag = FALSE
)
ccl_pfam_rspdr_g <- induced_subgraph(
ccl_pfam_rspdr_g,
V(ccl_pfam_rspdr_g)[
components(ccl_pfam_rspdr_g)$membership ==
which.max(components(ccl_pfam_rspdr_g)$csize)
]
)
ccl_pfam_rspdr_g_v_msk <- (
V(ccl_pfam_rspdr_g)$name %in% V(ccl_pfam_rspdr_g)$name
)
ccl_pfam_rspdr_p_g <- delete_edges(
ccl_pfam_rspdr_g,
E(ccl_pfam_rspdr_g)[E(ccl_pfam_rspdr_g)$weight < 0]
)
ccl_pfam_rspdr_p_g <- induced_subgraph(
ccl_pfam_rspdr_p_g,
V(ccl_pfam_rspdr_p_g)[
components(ccl_pfam_rspdr_p_g)$membership ==
which.max(components(ccl_pfam_rspdr_p_g)$csize)
]
)
ccl_pfam_rspdr_p_g_v_msk <- (
V(ccl_pfam_rspdr_g)$name %in% V(ccl_pfam_rspdr_p_g)$name
)
ccl_pfam_rspdr_n_g <- delete_edges(
ccl_pfam_rspdr_g,
E(ccl_pfam_rspdr_g)[E(ccl_pfam_rspdr_g)$weight > 0]
)
ccl_pfam_rspdr_n_g <- induced_subgraph(
ccl_pfam_rspdr_n_g,
V(ccl_pfam_rspdr_n_g)[
components(ccl_pfam_rspdr_n_g)$membership ==
which.max(components(ccl_pfam_rspdr_n_g)$csize)
]
)
ccl_pfam_rspdr_n_g_v_msk <- (
V(ccl_pfam_rspdr_g)$name %in% V(ccl_pfam_rspdr_n_g)$name
)
# Non-Responder
pfam_nspdr_data <-
pfam_data[patient_meta$Sample[patient_meta$ResponseBinary == 0], ]
# CCLasso takes many hours to run (load cached if available)
ccl_pfam_nspdr_file <- "data/ccl_pfam_nspdr_cv200.Rda"
if (!file.exists(ccl_pfam_nspdr_file)) {
ccl_pfam_nspdr <- cclasso(as.matrix(pfam_nspdr_data), counts = TRUE)
save(ccl_pfam_nspdr, file = ccl_pfam_nspdr_file)
} else {
load(ccl_pfam_nspdr_file)
}
ccl_pfam_nspdr$cor_w[is.nan(ccl_pfam_nspdr$cor_w)] <- 0
ccl_pfam_nspdr_pvals_vec <-
ccl_pfam_nspdr$p_vals[upper.tri(ccl_pfam_nspdr$p_vals)]
ccl_pfam_nspdr_pvals_adj <-
p.adjust(ccl_pfam_nspdr_pvals_vec, "BH")
ccl_pfam_nspdr_edges <-
ccl_pfam_nspdr$cor_w[upper.tri(ccl_pfam_nspdr$cor_w)]
# p-value < 1e-4 otherwise CCLasso network number of edges is very large
ccl_pfam_nspdr_edges[ccl_pfam_nspdr_pvals_adj > 1e-4] <- 0
ccl_pfam_nspdr_amat <-
matrix(0, dim(pfam_nspdr_data)[2], dim(pfam_nspdr_data)[2])
rownames(ccl_pfam_nspdr_amat) <- colnames(pfam_nspdr_data)
colnames(ccl_pfam_nspdr_amat) <- colnames(pfam_nspdr_data)
ccl_pfam_nspdr_amat[upper.tri(ccl_pfam_nspdr_amat)] <- ccl_pfam_nspdr_edges
ccl_pfam_nspdr_g <- graph_from_adjacency_matrix(
ccl_pfam_nspdr_amat,
mode = "upper",
weighted = TRUE,
diag = FALSE
)
ccl_pfam_nspdr_g <- induced_subgraph(
ccl_pfam_nspdr_g,
V(ccl_pfam_nspdr_g)[
components(ccl_pfam_nspdr_g)$membership ==
which.max(components(ccl_pfam_nspdr_g)$csize)
]
)
ccl_pfam_nspdr_g_v_msk <- (
V(ccl_pfam_nspdr_g)$name %in% V(ccl_pfam_nspdr_g)$name
)
ccl_pfam_nspdr_p_g <- delete_edges(
ccl_pfam_nspdr_g,
E(ccl_pfam_nspdr_g)[E(ccl_pfam_nspdr_g)$weight < 0]
)
ccl_pfam_nspdr_p_g <- induced_subgraph(
ccl_pfam_nspdr_p_g,
V(ccl_pfam_nspdr_p_g)[
components(ccl_pfam_nspdr_p_g)$membership ==
which.max(components(ccl_pfam_nspdr_p_g)$csize)
]
)
ccl_pfam_nspdr_p_g_v_msk <- (
V(ccl_pfam_nspdr_g)$name %in% V(ccl_pfam_nspdr_p_g)$name
)
ccl_pfam_nspdr_n_g <- delete_edges(
ccl_pfam_nspdr_g,
E(ccl_pfam_nspdr_g)[E(ccl_pfam_nspdr_g)$weight > 0]
)
ccl_pfam_nspdr_n_g <- induced_subgraph(
ccl_pfam_nspdr_n_g,
V(ccl_pfam_nspdr_n_g)[
components(ccl_pfam_nspdr_n_g)$membership ==
which.max(components(ccl_pfam_nspdr_n_g)$csize)
]
)
ccl_pfam_nspdr_n_g_v_msk <- (
V(ccl_pfam_nspdr_g)$name %in% V(ccl_pfam_nspdr_n_g)$name
)
Visualize Networks
ccl_pfam_rspdr_g_coord <- layout_with_lgl(ccl_pfam_rspdr_g)
ccl_pfam_rspdr_p_g_coord <-
ccl_pfam_rspdr_g_coord[ccl_pfam_rspdr_p_g_v_msk, , drop = F]
ccl_pfam_rspdr_n_g_coord <-
ccl_pfam_rspdr_g_coord[ccl_pfam_rspdr_n_g_v_msk, , drop = F]
ccl_pfam_nspdr_g_coord <- layout_with_lgl(ccl_pfam_nspdr_g)
ccl_pfam_nspdr_p_g_coord <-
ccl_pfam_nspdr_g_coord[ccl_pfam_nspdr_p_g_v_msk, , drop = F]
ccl_pfam_nspdr_n_g_coord <-
ccl_pfam_nspdr_g_coord[ccl_pfam_nspdr_n_g_v_msk, , drop = F]
par(mfrow = c(2, 3), mar = c(1, 1, 3, 1))
plot_network(ccl_pfam_rspdr_g,
coord = ccl_pfam_rspdr_g_coord,
main = "PFAM Responder CCLasso Network")
plot_network(ccl_pfam_rspdr_p_g,
coord = ccl_pfam_rspdr_p_g_coord,
main = "+ Interactions")

Network Statistics
Degree distribution, shortest paths distance distribution, transitivity:
par(mfrow = c(4, 2), mar = c(5.1, 4.1, 4.1, 2.1))
# degree distribution
k_r <- degree(ccl_pfam_rspdr_g)
hist(k_r,
breaks = 100,
xlab = "k",
ylab = "Frequency",
main = "R Degree Frequency")
k_n <- degree(ccl_pfam_nspdr_g)
hist(k_n,
breaks = 100,
xlab = "k",
ylab = NA,
main = "NR Degree Frequency")
# distance distribution of shortest paths
nv_r <- vcount(ccl_pfam_rspdr_g)
bfs_r_vec <- matrix(nrow = nv_r, ncol = nv_r)
for (i in 1:nv_r) {
bfs_r_vec[i, ] <- bfs(
ccl_pfam_rspdr_g,
root = i,
dist = TRUE,
unreachable = FALSE
)$dist
}
distd_r <- table(bfs_r_vec) / sum(bfs_r_vec, na.rm = TRUE)
distd_r <- tail(distd_r, length(distd_r) - 1)
plot(
names(distd_r),
distd_r,
xlab = "Distance",
ylab = expression(P[Distance]),
main = "R Distance Distribution of Shortest Paths"
)
nv_n <- vcount(ccl_pfam_nspdr_g)
bfs_n_vec <- matrix(nrow = nv_n, ncol = nv_n)
for (i in 1:nv_n) {
bfs_n_vec[i, ] <- bfs(
ccl_pfam_nspdr_g,
root = i,
dist = TRUE,
unreachable = FALSE
)$dist
}
distd_n <- table(bfs_n_vec) / sum(bfs_n_vec, na.rm = TRUE)
distd_n <- tail(distd_n, length(distd_n) - 1)
plot(
names(distd_n),
distd_n,
xlab = "Distance",
ylab = NA,
main = "NR Distance Distribution of Shortest Paths"
)
# clustering
cl_r <- transitivity(ccl_pfam_rspdr_g, type = "local")
cl_r_df <- data.frame(clust = cl_r, degree = k_r) %>%
group_by(degree) %>%
summarize(mclust = mean(clust, na.rm = TRUE))
plot(
mclust ~ degree,
data = cl_r_df,
xlab = "k",
ylab = "C(k)",
main = "R Clustering Coefficent"
)
cl_n <- transitivity(ccl_pfam_nspdr_g, type = "local")
cl_n_df <- data.frame(clust = cl_n, degree = k_n) %>%
group_by(degree) %>%
summarize(mclust = mean(clust, na.rm = TRUE))
plot(
mclust ~ degree,
data = cl_n_df,
xlab = "k",
ylab = NA,
main = "NR Clustering Coefficent"
)

Additional statistics:
Note: could not calculate betweenness and closeness on networks since it appears networks are too large and it crashes the R session
data.frame(
"Clustering coef." = c(
transitivity(ccl_pfam_rspdr_g, type = "global"),
transitivity(ccl_pfam_nspdr_g, type = "global")
),
"Power law coef." = c(
fit_power_law(k_r, xmin = 1)$alpha,
fit_power_law(k_n, xmin = 1)$alpha
),
"Mean shortest path" = c(
mean(bfs_r_vec, na.rm = TRUE),
mean(bfs_n_vec, na.rm = TRUE)
),
"Density" = c(
edge_density(ccl_pfam_rspdr_g),
edge_density(ccl_pfam_nspdr_g)
),
row.names = c("Responders", "Non-Responders")
)
Community Detection in Responder/Non-Responder Networks Using InfoMap and SpinGlass
num_top_clusts <- 3
# Responder +Network InfoMap
ccl_pfam_rspdr_p_g_imap <- cluster_infomap(ccl_pfam_rspdr_p_g)
ccl_pfam_rspdr_p_g_imap_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_rspdr_p_g_imap), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_rspdr_p_g_imap_top_v_msk <- (
ccl_pfam_rspdr_p_g_imap$membership %in% ccl_pfam_rspdr_p_g_imap_top_ids
)
ccl_pfam_rspdr_p_g_imap_top_g <- induced_subgraph(
ccl_pfam_rspdr_p_g,
V(ccl_pfam_rspdr_p_g)[ccl_pfam_rspdr_p_g_imap_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_rspdr_p_g_imap_top <- ccl_pfam_rspdr_p_g_imap
ccl_pfam_rspdr_p_g_imap_top$names <-
ccl_pfam_rspdr_p_g_imap_top$names[ccl_pfam_rspdr_p_g_imap_top_v_msk]
ccl_pfam_rspdr_p_g_imap_top$membership <-
ccl_pfam_rspdr_p_g_imap_top$membership[ccl_pfam_rspdr_p_g_imap_top_v_msk]
ccl_pfam_rspdr_p_g_imap_top$vcount <- length(ccl_pfam_rspdr_p_g_imap_top$names)
# Responder +Network SpinGlass
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_rspdr_p_g_spin_file <- "data/ccl_pfam_rspdr_p_g_spin.Rda"
if (!file.exists(ccl_pfam_rspdr_p_g_spin_file)) {
ccl_pfam_rspdr_p_g_spin <- cluster_spinglass(ccl_pfam_rspdr_p_g)
save(ccl_pfam_rspdr_p_g_spin, file = ccl_pfam_rspdr_p_g_spin_file)
} else {
load(ccl_pfam_rspdr_p_g_spin_file)
}
ccl_pfam_rspdr_p_g_spin_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_rspdr_p_g_spin), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_rspdr_p_g_spin_top_v_msk <- (
ccl_pfam_rspdr_p_g_spin$membership %in% ccl_pfam_rspdr_p_g_spin_top_ids
)
ccl_pfam_rspdr_p_g_spin_top_g <- induced_subgraph(
ccl_pfam_rspdr_p_g,
V(ccl_pfam_rspdr_p_g)[ccl_pfam_rspdr_p_g_spin_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_rspdr_p_g_spin_top <- ccl_pfam_rspdr_p_g_spin
ccl_pfam_rspdr_p_g_spin_top$names <-
ccl_pfam_rspdr_p_g_spin_top$names[ccl_pfam_rspdr_p_g_spin_top_v_msk]
ccl_pfam_rspdr_p_g_spin_top$membership <-
ccl_pfam_rspdr_p_g_spin_top$membership[ccl_pfam_rspdr_p_g_spin_top_v_msk]
ccl_pfam_rspdr_p_g_spin_top$vcount <- length(ccl_pfam_rspdr_p_g_spin_top$names)
# Responder +/-Network SpinGlass
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_rspdr_g_spin_file <- "data/ccl_pfam_rspdr_g_spin.Rda"
if (!file.exists(ccl_pfam_rspdr_g_spin_file)) {
ccl_pfam_rspdr_g_spin <- cluster_spinglass(ccl_pfam_rspdr_g,
implementation = "neg")
save(ccl_pfam_rspdr_g_spin, file = ccl_pfam_rspdr_g_spin_file)
} else {
load(ccl_pfam_rspdr_g_spin_file)
}
ccl_pfam_rspdr_g_spin_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_rspdr_g_spin), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_rspdr_g_spin_top_v_msk <- (
ccl_pfam_rspdr_g_spin$membership %in% ccl_pfam_rspdr_g_spin_top_ids
)
ccl_pfam_rspdr_g_spin_top_g <- induced_subgraph(
ccl_pfam_rspdr_g,
V(ccl_pfam_rspdr_g)[ccl_pfam_rspdr_g_spin_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_rspdr_g_spin_top <- ccl_pfam_rspdr_g_spin
ccl_pfam_rspdr_g_spin_top$names <-
ccl_pfam_rspdr_g_spin_top$names[ccl_pfam_rspdr_g_spin_top_v_msk]
ccl_pfam_rspdr_g_spin_top$membership <-
ccl_pfam_rspdr_g_spin_top$membership[ccl_pfam_rspdr_g_spin_top_v_msk]
ccl_pfam_rspdr_g_spin_top$vcount <- length(ccl_pfam_rspdr_g_spin_top$names)
# Non-Responder +Network InfoMap
ccl_pfam_nspdr_p_g_imap <- cluster_infomap(ccl_pfam_nspdr_p_g)
ccl_pfam_nspdr_p_g_imap_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_nspdr_p_g_imap), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_nspdr_p_g_imap_top_v_msk <- (
ccl_pfam_nspdr_p_g_imap$membership %in% ccl_pfam_nspdr_p_g_imap_top_ids
)
ccl_pfam_nspdr_p_g_imap_top_g <- induced_subgraph(
ccl_pfam_nspdr_p_g,
V(ccl_pfam_nspdr_p_g)[ccl_pfam_nspdr_p_g_imap_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_nspdr_p_g_imap_top <- ccl_pfam_nspdr_p_g_imap
ccl_pfam_nspdr_p_g_imap_top$names <-
ccl_pfam_nspdr_p_g_imap_top$names[ccl_pfam_nspdr_p_g_imap_top_v_msk]
ccl_pfam_nspdr_p_g_imap_top$membership <-
ccl_pfam_nspdr_p_g_imap_top$membership[ccl_pfam_nspdr_p_g_imap_top_v_msk]
ccl_pfam_nspdr_p_g_imap_top$vcount <- length(ccl_pfam_nspdr_p_g_imap_top$names)
# Non-Responder +Network SpinGlass
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_nspdr_p_g_spin_file <- "data/ccl_pfam_nspdr_p_g_spin.Rda"
if (!file.exists(ccl_pfam_nspdr_p_g_spin_file)) {
ccl_pfam_nspdr_p_g_spin <- cluster_spinglass(ccl_pfam_nspdr_p_g)
save(ccl_pfam_nspdr_p_g_spin, file = ccl_pfam_nspdr_p_g_spin_file)
} else {
load(ccl_pfam_nspdr_p_g_spin_file)
}
ccl_pfam_nspdr_p_g_spin_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_nspdr_p_g_spin), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_nspdr_p_g_spin_top_v_msk <- (
ccl_pfam_nspdr_p_g_spin$membership %in% ccl_pfam_nspdr_p_g_spin_top_ids
)
ccl_pfam_nspdr_p_g_spin_top_g <- induced_subgraph(
ccl_pfam_nspdr_p_g,
V(ccl_pfam_nspdr_p_g)[ccl_pfam_nspdr_p_g_spin_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_nspdr_p_g_spin_top <- ccl_pfam_nspdr_p_g_spin
ccl_pfam_nspdr_p_g_spin_top$names <-
ccl_pfam_nspdr_p_g_spin_top$names[ccl_pfam_nspdr_p_g_spin_top_v_msk]
ccl_pfam_nspdr_p_g_spin_top$membership <-
ccl_pfam_nspdr_p_g_spin_top$membership[ccl_pfam_nspdr_p_g_spin_top_v_msk]
ccl_pfam_nspdr_p_g_spin_top$vcount <- length(ccl_pfam_nspdr_p_g_spin_top$names)
# Non-Responder +/-Network SpinGlass
# SpinGlass takes a long time to run (load cached if available)
ccl_pfam_nspdr_g_spin_file <- "data/ccl_pfam_nspdr_g_spin.Rda"
if (!file.exists(ccl_pfam_nspdr_g_spin_file)) {
ccl_pfam_nspdr_g_spin <- cluster_spinglass(ccl_pfam_nspdr_g,
implementation = "neg")
save(ccl_pfam_nspdr_g_spin, file = ccl_pfam_nspdr_g_spin_file)
} else {
load(ccl_pfam_nspdr_g_spin_file)
}
ccl_pfam_nspdr_g_spin_top_ids <- sort(as.numeric(names(
sort(sizes(ccl_pfam_nspdr_g_spin), decreasing = TRUE)[1:num_top_clusts]
)))
ccl_pfam_nspdr_g_spin_top_v_msk <- (
ccl_pfam_nspdr_g_spin$membership %in% ccl_pfam_nspdr_g_spin_top_ids
)
ccl_pfam_nspdr_g_spin_top_g <- induced_subgraph(
ccl_pfam_nspdr_g,
V(ccl_pfam_nspdr_g)[ccl_pfam_nspdr_g_spin_top_v_msk]
)
# iGraph has no functionality to subset community objects so hack it
ccl_pfam_nspdr_g_spin_top <- ccl_pfam_nspdr_g_spin
ccl_pfam_nspdr_g_spin_top$names <-
ccl_pfam_nspdr_g_spin_top$names[ccl_pfam_nspdr_g_spin_top_v_msk]
ccl_pfam_nspdr_g_spin_top$membership <-
ccl_pfam_nspdr_g_spin_top$membership[ccl_pfam_nspdr_g_spin_top_v_msk]
ccl_pfam_nspdr_g_spin_top$vcount <- length(ccl_pfam_nspdr_g_spin_top$names)
par(mfrow = c(6, 2), mar = c(1, 1, 3, 1))
plot(
ccl_pfam_rspdr_p_g_imap,
ccl_pfam_rspdr_p_g,
layout = ccl_pfam_rspdr_p_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM Responder CCLasso +Network InfoMap Clusters"
)
plot(
ccl_pfam_rspdr_p_g_imap_top,
ccl_pfam_rspdr_p_g_imap_top_g,
col = membership(ccl_pfam_rspdr_p_g_imap)[ccl_pfam_rspdr_p_g_imap_top_v_msk],
layout = ccl_pfam_rspdr_p_g_coord[ccl_pfam_rspdr_p_g_imap_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(
ccl_pfam_rspdr_p_g_imap
)), alpha = 1)[ccl_pfam_rspdr_p_g_imap_top_ids],
mark.col = rainbow(length(communities(
ccl_pfam_rspdr_p_g_imap
)), alpha = 0.3)[ccl_pfam_rspdr_p_g_imap_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_nspdr_p_g_imap,
ccl_pfam_nspdr_p_g,
layout = ccl_pfam_nspdr_p_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM Non-Responder CCLasso +Network InfoMap Clusters"
)
plot(
ccl_pfam_nspdr_p_g_imap_top,
ccl_pfam_nspdr_p_g_imap_top_g,
col = membership(ccl_pfam_nspdr_p_g_imap)[ccl_pfam_nspdr_p_g_imap_top_v_msk],
layout = ccl_pfam_nspdr_p_g_coord[ccl_pfam_nspdr_p_g_imap_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(
ccl_pfam_nspdr_p_g_imap
)), alpha = 1)[ccl_pfam_nspdr_p_g_imap_top_ids],
mark.col = rainbow(length(communities(
ccl_pfam_nspdr_p_g_imap
)), alpha = 0.3)[ccl_pfam_nspdr_p_g_imap_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_rspdr_p_g_spin,
ccl_pfam_rspdr_p_g,
layout = ccl_pfam_rspdr_p_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM Responder CCLasso +Network SpinGlass Clusters"
)
plot(
ccl_pfam_rspdr_p_g_spin_top,
ccl_pfam_rspdr_p_g_spin_top_g,
col = membership(ccl_pfam_rspdr_p_g_spin)[ccl_pfam_rspdr_p_g_spin_top_v_msk],
layout = ccl_pfam_rspdr_p_g_coord[ccl_pfam_rspdr_p_g_spin_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(
ccl_pfam_rspdr_p_g_spin
)),
alpha = 1)[ccl_pfam_rspdr_p_g_spin_top_ids],
mark.col = rainbow(length(communities(
ccl_pfam_rspdr_p_g_spin
)),
alpha = 0.3)[ccl_pfam_rspdr_p_g_spin_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_nspdr_p_g_spin,
ccl_pfam_nspdr_p_g,
layout = ccl_pfam_nspdr_p_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM Non-Responder CCLasso +Network SpinGlass Clusters"
)
plot(
ccl_pfam_nspdr_p_g_spin_top,
ccl_pfam_nspdr_p_g_spin_top_g,
col = membership(ccl_pfam_nspdr_p_g_spin)[ccl_pfam_nspdr_p_g_spin_top_v_msk],
layout = ccl_pfam_nspdr_p_g_coord[ccl_pfam_nspdr_p_g_spin_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(
ccl_pfam_nspdr_p_g_spin
)),
alpha = 1)[ccl_pfam_nspdr_p_g_spin_top_ids],
mark.col = rainbow(length(communities(
ccl_pfam_nspdr_p_g_spin
)),
alpha = 0.3)[ccl_pfam_nspdr_p_g_spin_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_rspdr_g_spin,
ccl_pfam_rspdr_g,
layout = ccl_pfam_rspdr_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM Responder CCLasso +/-Network SpinGlass Clusters"
)
plot(
ccl_pfam_rspdr_g_spin_top,
ccl_pfam_rspdr_g_spin_top_g,
col = membership(ccl_pfam_rspdr_g_spin)[ccl_pfam_rspdr_g_spin_top_v_msk],
layout = ccl_pfam_rspdr_g_coord[ccl_pfam_rspdr_g_spin_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(
ccl_pfam_rspdr_g_spin
)),
alpha = 1)[ccl_pfam_rspdr_g_spin_top_ids],
mark.col = rainbow(length(communities(
ccl_pfam_rspdr_g_spin
)),
alpha = 0.3)[ccl_pfam_rspdr_g_spin_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)
plot(
ccl_pfam_nspdr_g_spin,
ccl_pfam_nspdr_g,
layout = ccl_pfam_nspdr_g_coord,
vertex.size = 4,
vertex.label.cex = 0.25,
main = "PFAM Non-Responder CCLasso +/-Network SpinGlass Clusters"
)
plot(
ccl_pfam_nspdr_g_spin_top,
ccl_pfam_nspdr_g_spin_top_g,
col = membership(ccl_pfam_nspdr_g_spin)[ccl_pfam_nspdr_g_spin_top_v_msk],
layout = ccl_pfam_nspdr_g_coord[ccl_pfam_nspdr_g_spin_top_v_msk, , drop = F],
mark.border = rainbow(length(communities(
ccl_pfam_nspdr_g_spin
)),
alpha = 1)[ccl_pfam_nspdr_g_spin_top_ids],
mark.col = rainbow(length(communities(
ccl_pfam_nspdr_g_spin
)),
alpha = 0.3)[ccl_pfam_nspdr_g_spin_top_ids],
vertex.size = 4,
vertex.label.cex = 0.25,
main = paste("Top", num_top_clusts, "Clusters")
)

Save cluster results:
# Responder
ccl_pfam_rspdr_p_g_imap_data <- data.frame()
for (i in 1:max(ccl_pfam_rspdr_p_g_imap$membership)) {
ccl_pfam_rspdr_p_g_imap_data <- rbind(
ccl_pfam_rspdr_p_g_imap_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_rspdr_p_g)$name[
ccl_pfam_rspdr_p_g_imap$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_rspdr_p_g_imap_data <-
merge.easy(ccl_pfam_rspdr_p_g_imap_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_rspdr_p_g_imap_data,
file = "results/PfamRpr_CCLassoPosNet_InfoMapClusters.csv",
row.names = FALSE)
ccl_pfam_rspdr_p_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_rspdr_p_g_spin$membership)) {
ccl_pfam_rspdr_p_g_spin_data <- rbind(
ccl_pfam_rspdr_p_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_rspdr_p_g)$name[
ccl_pfam_rspdr_p_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_rspdr_p_g_spin_data <-
merge.easy(ccl_pfam_rspdr_p_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_rspdr_p_g_spin_data,
file = "results/PfamRpr_CCLassoPosNet_SpinGlassClusters.csv",
row.names = FALSE)
ccl_pfam_rspdr_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_rspdr_g_spin$membership)) {
ccl_pfam_rspdr_g_spin_data <- rbind(
ccl_pfam_rspdr_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_rspdr_g)$name[
ccl_pfam_rspdr_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_rspdr_g_spin_data <-
merge.easy(ccl_pfam_rspdr_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_rspdr_g_spin_data,
file = "results/PfamRpr_CCLassoNet_SpinGlassClusters.csv")
# Non-Responder
ccl_pfam_nspdr_p_g_imap_data <- data.frame()
for (i in 1:max(ccl_pfam_nspdr_p_g_imap$membership)) {
ccl_pfam_nspdr_p_g_imap_data <- rbind(
ccl_pfam_nspdr_p_g_imap_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_nspdr_p_g)$name[
ccl_pfam_nspdr_p_g_imap$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_nspdr_p_g_imap_data <-
merge.easy(ccl_pfam_nspdr_p_g_imap_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_nspdr_p_g_imap_data,
file = "results/PfamNpr_CCLassoPosNet_InfoMapClusters.csv",
row.names = FALSE)
ccl_pfam_nspdr_p_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_nspdr_p_g_spin$membership)) {
ccl_pfam_nspdr_p_g_spin_data <- rbind(
ccl_pfam_nspdr_p_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_nspdr_p_g)$name[
ccl_pfam_nspdr_p_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_nspdr_p_g_spin_data <-
merge.easy(ccl_pfam_nspdr_p_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_nspdr_p_g_spin_data,
file = "results/PfamNpr_CCLassoPosNet_SpinGlassClusters.csv",
row.names = FALSE)
ccl_pfam_nspdr_g_spin_data <- data.frame()
for (i in 1:max(ccl_pfam_nspdr_g_spin$membership)) {
ccl_pfam_nspdr_g_spin_data <- rbind(
ccl_pfam_nspdr_g_spin_data,
data.frame(
Cluster = i,
Accession = V(ccl_pfam_nspdr_g)$name[
ccl_pfam_nspdr_g_spin$membership == i
],
stringsAsFactors = FALSE
)
)
}
ccl_pfam_nspdr_g_spin_data <-
merge.easy(ccl_pfam_nspdr_g_spin_data, pfam_feat, key = "Accession")
write.csv(ccl_pfam_nspdr_g_spin_data,
file = "results/PfamNpr_CCLassoNet_SpinGlassClusters.csv",
row.names = FALSE)
LS0tCnRpdGxlOiAiQ01TQyA4MjhPIFNlbWVzdGVyIFByb2plY3Q6IENhbmNlciBNaWNyb2Jpb21lIGFudGktUEQxIFRoZXJhcHkgUEZBTSBDQ0xhc3NvIE5ldHdvcmsgQW5hbHlzaXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICB0aGVtZTogY29zbW8KICBwZGZfZG9jdW1lbnQ6CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IGthYmxlCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIHRoZW1lOiBjb3NtbwplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRSwgY2FjaGUgPSBGQUxTRX0Kb3B0aW9ucyhtYXgucHJpbnQgPSAyMDApCm9wdGlvbnModGlueXRleC52ZXJib3NlID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLAogIGNhY2hlID0gVFJVRSwKICBmaWcuc2hvdyA9ICJob2xkIiwKICB0aWR5ID0gRkFMU0UsCiAgdGlkeS5vcHRzID0gbGlzdCh3aWR0aC5jdXRvZmYgPSA4MCksCiAgbGluZXdpZHRoID0gODAKKQpob29rX291dHB1dCA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgib3V0cHV0IikKa25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICMgdGhpcyBob29rIGlzIHVzZWQgb25seSB3aGVuIHRoZSBsaW5ld2lkdGggb3B0aW9uIGlzIG5vdCBOVUxMCiAgICBpZiAoIWlzLm51bGwobiA8LSBvcHRpb25zJGxpbmV3aWR0aCkpIHsKICAgICAgeCA9IGtuaXRyOjo6c3BsaXRfbGluZXMoeCkKICAgICAgIyBhbnkgbGluZXMgd2lkZXIgdGhhbiBuIHNob3VsZCBiZSB3cmFwcGVkCiAgICAgIGlmIChhbnkobmNoYXIoeCkgPiBuKSkKICAgICAgICB4ID0gc3Ryd3JhcCh4LCB3aWR0aCA9IG4pCiAgICAgIHggPSBwYXN0ZSh4LCBjb2xsYXBzZSA9ICJcbiIpCiAgICB9CiAgICBob29rX291dHB1dCh4LCBvcHRpb25zKQogIH0KKQpgYGAKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShpZ3JhcGgpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5cikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGRhdGEudGFibGUpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoc291cmNlKCJDQ0xhc3NvLlIiKSkKCnNldC5zZWVkKDE5ODI1NzkxKQoKIyBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24KY28udmFyIDwtIGZ1bmN0aW9uKHgsIG5hLnJtID0gVFJVRSkgewogIDEwMCAqIChzZCh4LCBuYS5ybSA9IG5hLnJtKSAvIG1lYW4oeCwgbmEucm0gPSBuYS5ybSkpCn0KCiMga2poZWFseS9wb2xhci1sYWJlbHMuciBvbiBnaXRodWI6CiMgaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20va2poZWFseS84MzQ3NzQjZmlsZS1wb2xhci1sYWJlbHMtcgpyYWRpYW4ucmVzY2FsZSA8LSBmdW5jdGlvbih4LAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9IDEpIHsKICBjLnJvdGF0ZSA8LSBmdW5jdGlvbih4KSAoeCArIHN0YXJ0KSAlJSAoMiAqIHBpKSAqIGRpcmVjdGlvbgogIGMucm90YXRlKHNjYWxlczo6cmVzY2FsZSh4LCBjKDAsIDIgKiBwaSksIHJhbmdlKHgpKSkKfQoKbWVyZ2UuZWFzeSA8LSBmdW5jdGlvbihkZjEsIGRmMiwga2V5KSB7CiAgZGYxIDwtIGRhdGEudGFibGUoZGYxLCBrZXkgPSBrZXkpCiAgZGYyIDwtIGRhdGEudGFibGUoZGYyLCBrZXkgPSBrZXkpCiAgcmV0dXJuKGFzLmRhdGEuZnJhbWUodW5pcXVlKAogICAgbWVyZ2UoCiAgICAgIGRmMSwKICAgICAgZGYyLAogICAgICBhbGwueCA9IFRSVUUsCiAgICAgIGJ5ID0gLkVBQ0hJLAogICAgICBhbGxvdy5jYXJ0ZXNpYW4gPSBUUlVFCiAgICApCiAgKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSkKfQoKIyBxdWFydGlsZSBjb2VmZmljaWVudCBvZiBkaXNwZXJzaW9uCnFjZCA8LSBmdW5jdGlvbih4KSB7CiAgKHF1YW50aWxlKHgsIDAuNzUpIC0gcXVhbnRpbGUoeCwgMC4yNSkpIC8KICAocXVhbnRpbGUoeCwgMC43NSkgKyBxdWFudGlsZSh4LCAwLjI1KSkKfQoKcGxvdF9uZXR3b3JrIDwtIGZ1bmN0aW9uKGlnLCBjb29yZCA9IGxheW91dF93aXRoX2ZyKGlnKSwgLi4uKSB7CiAgcGxvdCgKICAgIGlnLAogICAgbGF5b3V0ID0gY29vcmQsCiAgICB2ZXJ0ZXguc2l6ZSA9IDIsCiAgICB2ZXJ0ZXgubGFiZWwgPSBWKGlnKSRuYW1lLAogICAgdmVydGV4LmxhYmVsLmRpc3QgPSAxLAogICAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgICB2ZXJ0ZXgubGFiZWwuZGVncmVlID0gcmFkaWFuLnJlc2NhbGUoCiAgICAgIHggPSAxOnZjb3VudChpZyksCiAgICAgIGRpcmVjdGlvbiA9IC0xLAogICAgICBzdGFydCA9IDAKICAgICksCiAgICBlZGdlLmNvbG9yID0gaWZlbHNlKEUoaWcpJHdlaWdodCA+IDAsICdncmVlbicsICdyZWQnKSwKICAgIC4uLgogICkKfQpgYGAKCiMjIyBMb2FkIERhdGEgYW5kIE1ldGFkYXRhCgpgYGB7cn0KcGZhbV9kYXRhIDwtCiAgcmVhZC5kZWxpbSgiZGF0YS95YW1zX3BkMV9wdWJfcGZhbV9wcG0udHh0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpyb3duYW1lcyhwZmFtX2RhdGEpIDwtIHBmYW1fZGF0YSRGZWF0dXJlCnBmYW1fZGF0YSRGZWF0dXJlIDwtIE5VTEwKcGZhbV9kYXRhIDwtIGFzLmRhdGEuZnJhbWUodChwZmFtX2RhdGEpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiMgcmVtb3ZlIGZlYXR1cmVzIHdpdGggZmV3ZXIgdGhhbiAxMCBvYnNlcnZhdGlvbnMKcGZhbV9kYXRhIDwtIHBmYW1fZGF0YVssIGNvbFN1bXMocGZhbV9kYXRhICE9IDApID4gMTBdCnBmYW1fZmVhdCA8LQogIHJlYWQuZGVsaW0oImRhdGEveWFtc19wZDFfcHViX3BmYW1fZmVhdC50eHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnBhdGllbnRfbWV0YSA8LQogIHJlYWQuZGVsaW0oImRhdGEveWFtc19wZDFfcHViX21ldGEudHh0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpwYXRpZW50X21ldGEkUmVzcG9uc2VCaW5hcnkgPC0KICBhcy5udW1lcmljKGZhY3RvcigocGF0aWVudF9tZXRhJENsaW5fUmVzcG9uc2UpKSkgLSAxCnBhdGllbnRfbWV0YSA8LSBwYXRpZW50X21ldGFbLCBjKCJTYW1wbGUiLCAiUmVzcG9uc2VCaW5hcnkiKV0KcGF0aWVudF9tZXRhJFNhbXBsZSA8LSBnc3ViKCItIiwgIi4iLCBwYXRpZW50X21ldGEkU2FtcGxlKQpgYGAKCiMjIyBWYXJpYW5jZSBhbmQgQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIFN1bW1hcnkgU3RhdGlzdGljcwoKYGBge3J9CnN1bW1hcnkobG0obG9nKGFwcGx5KHBmYW1fZGF0YSwgMiwgdmFyKSkgfiBsb2coYXBwbHkoCiAgcGZhbV9kYXRhLCAyLCBtZWFuLCBuYS5ybSA9IFRSVUUKKSkpKQpzdW1tYXJ5KGxtKGxvZyhhcHBseShwZmFtX2RhdGEsIDIsIGNvLnZhcikpIH4gbG9nKGFwcGx5KAogIHBmYW1fZGF0YSwgMiwgbWVhbiwgbmEucm0gPSBUUlVFCikpKSkKYGBgCmBgYHtyLCBmaWcuaGVpZ2h0PTQuNSwgZmlnLndpZHRoPTEwfQpwYXIobWZyb3cgPSBjKDEsIDIpLCBtYXIgPSBjKDUuMSwgNC4xLCA0LjEsIDIuMSkpCnBsb3QoCiAgYXBwbHkocGZhbV9kYXRhLCAyLCBtZWFuLCBuYS5ybSA9IFRSVUUpLAogIGFwcGx5KHBmYW1fZGF0YSwgMiwgdmFyKSwKICBsb2cgPSAieHkiLAogIHhsYWIgPSAiTWVhbiIsCiAgeWxhYiA9ICJWYXJpYW5jZSIKKQpwbG90KAogIGFwcGx5KHBmYW1fZGF0YSwgMiwgbWVhbiwgbmEucm0gPSBUUlVFKSwKICBhcHBseShwZmFtX2RhdGEsIDIsIGNvLnZhciksCiAgbG9nID0gInh5IiwKICB4bGFiID0gIk1lYW4iLAogIHlsYWIgPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIgopCmBgYApgYGB7ciwgZmlnLmhlaWdodD00LjUsIGZpZy53aWR0aD0xMH0KcGFyKG1mcm93ID0gYygxLCAyKSwgbWFyID0gYyg1LjEsIDQuMSwgNC4xLCAyLjEpKQojIGhpc3QoYXBwbHkocGZhbV9kYXRhLCAyLCBtZWFuLCBuYS5ybSA9IFRSVUUpKQpoaXN0KAogIGFwcGx5KHBmYW1fZGF0YSwgMiwgY28udmFyKSwKICBicmVha3MgPSAxMDAsCiAgeGxhYiA9ICJDb2VmZmljaWVudCBvZiBWYXJpYXRpb24iLAogIG1haW4gPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIEhpc3RvZ3JhbSIKKQpwbG90KGRlbnNpdHkoYXBwbHkocGZhbV9kYXRhLCAyLCBjby52YXIpKSwKICAgICBtYWluID0gIkNvZWZmaWNpZW50IG9mIFZhcmlhdGlvbiBEZW5zaXR5IikKYWJsaW5lKHYgPSAyMDAsIGNvbCA9ICJyZWQiKQpgYGAKCiMjIyMgUmVtb3ZlIGZlYXR1cmVzIHdpdGggbG93IGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbjoKCmBgYHtyfQpwZmFtX2RhdGEgPC0gcGZhbV9kYXRhWywgYXBwbHkocGZhbV9kYXRhLCAyLCBjby52YXIpID4gMjAwXQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YS5mcmFtZSgKICBNZWFuID0gYXBwbHkocGZhbV9kYXRhLCAyLCBtZWFuLCBuYS5ybSA9IFRSVUUpLAogIENvZWZWYXIgPSBhcHBseShwZmFtX2RhdGEsIDIsIGNvLnZhcikKKSwgYWVzKHggPSBNZWFuLCB5ID0gQ29lZlZhcikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9kZW5zaXR5MmQoKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKGxpbWl0cyA9IGMoMTgwLCAxMTAwKSkKYGBgCgojIyMgQnVpbGQgTmV0d29yawoKYGBge3J9CiMgQ0NMYXNzbyB0YWtlcyBtYW55IGhvdXJzIHRvIHJ1biAobG9hZCBjYWNoZWQgaWYgYXZhaWxhYmxlKQpjY2xfcGZhbV9maWxlIDwtICJkYXRhL2NjbF9wZmFtX2N2MjAwLlJkYSIKaWYgKCFmaWxlLmV4aXN0cyhjY2xfcGZhbV9maWxlKSkgewogIGNjbF9wZmFtIDwtIGNjbGFzc28oYXMubWF0cml4KHBmYW1fZGF0YSksIGNvdW50cyA9IFRSVUUpCiAgc2F2ZShjY2xfcGZhbSwgZmlsZSA9IGNjbF9wZmFtX2ZpbGUpCn0gZWxzZSB7CiAgbG9hZChjY2xfcGZhbV9maWxlKQp9CmNjbF9wZmFtJGNvcl93W2lzLm5hbihjY2xfcGZhbSRjb3JfdyldIDwtIDAKY2NsX3BmYW1fcHZhbHNfdmVjIDwtIGNjbF9wZmFtJHBfdmFsc1t1cHBlci50cmkoY2NsX3BmYW0kcF92YWxzKV0KY2NsX3BmYW1fcHZhbHNfYWRqIDwtIHAuYWRqdXN0KGNjbF9wZmFtX3B2YWxzX3ZlYywgIkJIIikKY2NsX3BmYW1fZWRnZXMgPC0gY2NsX3BmYW0kY29yX3dbdXBwZXIudHJpKGNjbF9wZmFtJGNvcl93KV0KIyBwLXZhbHVlIDwgMWUtNCBvdGhlcndpc2UgQ0NMYXNzbyBuZXR3b3JrIG51bWJlciBvZiBlZGdlcyBpcyB2ZXJ5IGxhcmdlCmNjbF9wZmFtX2VkZ2VzW2NjbF9wZmFtX3B2YWxzX2FkaiA+IDFlLTRdIDwtIDAKY2NsX3BmYW1fYW1hdCA8LSBtYXRyaXgoMCwgZGltKHBmYW1fZGF0YSlbMl0sIGRpbShwZmFtX2RhdGEpWzJdKQpyb3duYW1lcyhjY2xfcGZhbV9hbWF0KSA8LSBjb2xuYW1lcyhwZmFtX2RhdGEpCmNvbG5hbWVzKGNjbF9wZmFtX2FtYXQpIDwtIGNvbG5hbWVzKHBmYW1fZGF0YSkKY2NsX3BmYW1fYW1hdFt1cHBlci50cmkoY2NsX3BmYW1fYW1hdCldIDwtIGNjbF9wZmFtX2VkZ2VzCmNjbF9wZmFtX2cgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KAogIGNjbF9wZmFtX2FtYXQsCiAgbW9kZSA9ICJ1cHBlciIsCiAgd2VpZ2h0ZWQgPSBUUlVFLAogIGRpYWcgPSBGQUxTRQopCmNjbF9wZmFtX2cgPC0gaW5kdWNlZF9zdWJncmFwaCgKICBjY2xfcGZhbV9nLAogIFYoY2NsX3BmYW1fZylbCiAgICBjb21wb25lbnRzKGNjbF9wZmFtX2cpJG1lbWJlcnNoaXAgPT0KICAgIHdoaWNoLm1heChjb21wb25lbnRzKGNjbF9wZmFtX2cpJGNzaXplKQogIF0KKQpjY2xfcGZhbV9wX2cgPC0gZGVsZXRlX2VkZ2VzKAogIGNjbF9wZmFtX2csCiAgRShjY2xfcGZhbV9nKVtFKGNjbF9wZmFtX2cpJHdlaWdodCA8IDBdCikKY2NsX3BmYW1fcF9nIDwtIGluZHVjZWRfc3ViZ3JhcGgoCiAgY2NsX3BmYW1fcF9nLAogIFYoY2NsX3BmYW1fcF9nKVsKICAgIGNvbXBvbmVudHMoY2NsX3BmYW1fcF9nKSRtZW1iZXJzaGlwID09CiAgICB3aGljaC5tYXgoY29tcG9uZW50cyhjY2xfcGZhbV9wX2cpJGNzaXplKQogIF0KKQpjY2xfcGZhbV9wX2dfdl9tc2sgPC0gKAogIFYoY2NsX3BmYW1fZykkbmFtZSAlaW4lIFYoY2NsX3BmYW1fcF9nKSRuYW1lCikKY2NsX3BmYW1fbl9nIDwtIGRlbGV0ZV9lZGdlcygKICBjY2xfcGZhbV9nLAogIEUoY2NsX3BmYW1fZylbRShjY2xfcGZhbV9nKSR3ZWlnaHQgPiAwXQopCmNjbF9wZmFtX25fZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX25fZywKICBWKGNjbF9wZmFtX25fZylbCiAgICBjb21wb25lbnRzKGNjbF9wZmFtX25fZykkbWVtYmVyc2hpcCA9PQogICAgd2hpY2gubWF4KGNvbXBvbmVudHMoY2NsX3BmYW1fbl9nKSRjc2l6ZSkKICBdCikKY2NsX3BmYW1fbl9nX3ZfbXNrIDwtICgKICBWKGNjbF9wZmFtX2cpJG5hbWUgJWluJSBWKGNjbF9wZmFtX25fZykkbmFtZQopCmBgYAoKIyMjIFZpc3VhbGl6ZSBOZXR3b3JrCgpgYGB7ciwgZmlnLmhlaWdodD00LjUsIGZpZy53aWR0aD0xMH0KY2NsX3BmYW1fZ19jb29yZCA8LSBsYXlvdXRfd2l0aF9sZ2woY2NsX3BmYW1fZykKY2NsX3BmYW1fcF9nX2Nvb3JkIDwtIGNjbF9wZmFtX2dfY29vcmRbY2NsX3BmYW1fcF9nX3ZfbXNrLCAsIGRyb3AgPSBGXQpjY2xfcGZhbV9uX2dfY29vcmQgPC0gY2NsX3BmYW1fZ19jb29yZFtjY2xfcGZhbV9uX2dfdl9tc2ssICwgZHJvcCA9IEZdCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoMSwgMSwgMywgMSkpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICJQRkFNIENDTGFzc28gTmV0d29yayIpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9wX2csCiAgICAgICAgICAgICBjb29yZCA9IGNjbF9wZmFtX3BfZ19jb29yZCwKICAgICAgICAgICAgIG1haW4gPSAiKyBJbnRlcmFjdGlvbnMiKQpwbG90X25ldHdvcmsoY2NsX3BmYW1fbl9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9uX2dfY29vcmQsCiAgICAgICAgICAgICBtYWluID0gIi0gSW50ZXJhY3Rpb25zIikKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpwbmcoCiAgInJlc3VsdHMvUGZhbV9DQ0xhc3NvTmV0LnBuZyIsCiAgaGVpZ2h0ID0gNC41LAogIHdpZHRoID0gMTAsCiAgdW5pdHMgPSAiaW4iLAogIHJlcyA9IDMwMAopCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoMSwgMSwgMywgMSkpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICJQRkFNIENDTGFzc28gTmV0d29yayIpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9wX2csCiAgICAgICAgICAgICBjb29yZCA9IGNjbF9wZmFtX3BfZ19jb29yZCwKICAgICAgICAgICAgIG1haW4gPSAiKyBJbnRlcmFjdGlvbnMiKQpwbG90X25ldHdvcmsoY2NsX3BmYW1fbl9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9uX2dfY29vcmQsCiAgICAgICAgICAgICBtYWluID0gIi0gSW50ZXJhY3Rpb25zIikKaW52aXNpYmxlKGRldi5vZmYoKSkKYGBgCgojIyMgTmV0d29yayBTdGF0aXN0aWNzCgojIyMjIERlZ3JlZSBkaXN0cmlidXRpb24sIHNob3J0ZXN0IHBhdGhzIGRpc3RhbmNlIGRpc3RyaWJ1dGlvbiwgdHJhbnNpdGl2aXR5OgoKYGBge3IsIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTEwfQpwYXIobWZyb3cgPSBjKDIsIDIpLCBtYXIgPSBjKDUuMSwgNC4xLCA0LjEsIDIuMSkpCiMgZGVncmVlIGRpc3RyaWJ1dGlvbgprIDwtIGRlZ3JlZShjY2xfcGZhbV9nKQpoaXN0KGssCiAgICAgYnJlYWtzID0gMTAwLAogICAgIHhsYWIgPSAiayIsCiAgICAgeWxhYiA9ICJGcmVxdWVuY3kiLAogICAgIG1haW4gPSAiRGVncmVlIEhpc3RvZ3JhbSIpCmRkIDwtIGRlZ3JlZV9kaXN0cmlidXRpb24oY2NsX3BmYW1fZykKZGRfbnplcm9fcG9zIDwtIHdoaWNoKGRkICE9IDApCnBrIDwtIGRkW2RkX256ZXJvX3Bvc10KZHggPC0gMTptYXgoaykKZHggPC0gZHhbZGRfbnplcm9fcG9zXQpwbG90KAogIHBrIH4gbG9nKGR4KSwKICBsb2cgPSAieHkiLAogIHhsYWIgPSAibG9nIGsiLAogIHlsYWIgPSAibG9nIFBrIiwKICBtYWluID0gIkxvZy1Mb2cgRGVncmVlIERpc3RyaWJ1dGlvbiIKKQojIGRpc3RhbmNlIGRpc3RyaWJ1dGlvbiBvZiBzaG9ydGVzdCBwYXRocwpudiA8LSB2Y291bnQoY2NsX3BmYW1fZykKYmZzX3ZlYyA8LSBtYXRyaXgobnJvdyA9IG52LCBuY29sID0gbnYpCmZvciAoaSBpbiAxOm52KSB7CiAgYmZzX3ZlY1tpLCBdIDwtIGJmcygKICAgIGNjbF9wZmFtX2csCiAgICByb290ID0gaSwKICAgIGRpc3QgPSBUUlVFLAogICAgdW5yZWFjaGFibGUgPSBGQUxTRQogICkkZGlzdAp9CmRpc3RkIDwtIHRhYmxlKGJmc192ZWMpIC8gc3VtKGJmc192ZWMsIG5hLnJtID0gVFJVRSkKZGlzdGQgPC0gdGFpbChkaXN0ZCwgbGVuZ3RoKGRpc3RkKSAtIDEpCnBsb3QoCiAgbmFtZXMoZGlzdGQpLAogIGRpc3RkLAogIHhsYWIgPSAiRGlzdGFuY2UiLAogIHlsYWIgPSBleHByZXNzaW9uKFBbRGlzdGFuY2VdKSwKICBtYWluID0gIkRpc3RhbmNlIERpc3RyaWJ1dGlvbiBvZiBTaG9ydGVzdCBQYXRocyIKKQojIGNsdXN0ZXJpbmcKY2wgPC0gdHJhbnNpdGl2aXR5KGNjbF9wZmFtX2csIHR5cGUgPSAibG9jYWwiKQpjbF9kZiA8LSBkYXRhLmZyYW1lKGNsdXN0ID0gY2wsIGRlZ3JlZSA9IGspICU+JQogIGdyb3VwX2J5KGRlZ3JlZSkgJT4lCiAgc3VtbWFyaXplKG1jbHVzdCA9IG1lYW4oY2x1c3QsIG5hLnJtID0gVFJVRSkpCnBsb3QoCiAgbWNsdXN0IH4gZGVncmVlLAogIGRhdGEgPSBjbF9kZiwKICB4bGFiID0gImsiLAogIHlsYWIgPSAiQyhrKSIsCiAgbWFpbiA9ICJDbHVzdGVyaW5nIENvZWZmaWNlbnQiCikKYGBgCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnBuZygKICAicmVzdWx0cy9QZmFtX0NDTGFzc29OZXRfU3RhdHMucG5nIiwKICBoZWlnaHQgPSA5LAogIHdpZHRoID0gMTAsCiAgdW5pdHMgPSAiaW4iLAogIHJlcyA9IDMwMAopCnBhcihtZnJvdyA9IGMoMiwgMiksIG1hciA9IGMoNS4xLCA0LjEsIDQuMSwgMi4xKSkKaGlzdChrLAogICAgIGJyZWFrcyA9IDEwMCwKICAgICB4bGFiID0gImsiLAogICAgIHlsYWIgPSAiRnJlcXVlbmN5IiwKICAgICBtYWluID0gIkRlZ3JlZSBIaXN0b2dyYW0iKQpwbG90KAogIHBrIH4gbG9nKGR4KSwKICBsb2cgPSAieHkiLAogIHhsYWIgPSAibG9nIGsiLAogIHlsYWIgPSAibG9nIFBrIiwKICBtYWluID0gIkxvZy1Mb2cgRGVncmVlIERpc3RyaWJ1dGlvbiIKKQpwbG90KAogIG5hbWVzKGRpc3RkKSwKICBkaXN0ZCwKICB4bGFiID0gIkRpc3RhbmNlIiwKICB5bGFiID0gZXhwcmVzc2lvbihQW0Rpc3RhbmNlXSksCiAgbWFpbiA9ICJEaXN0YW5jZSBEaXN0cmlidXRpb24gb2YgU2hvcnRlc3QgUGF0aHMiCikKcGxvdCgKICBtY2x1c3QgfiBkZWdyZWUsCiAgZGF0YSA9IGNsX2RmLAogIHhsYWIgPSAiayIsCiAgeWxhYiA9ICJDKGspIiwKICBtYWluID0gIkNsdXN0ZXJpbmcgQ29lZmZpY2VudCIKKQppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCiMjIyMgQWRkaXRpb25hbCBzdGF0aXN0aWNzOgoKYGBge3J9CmRhdGEuZnJhbWUoCiAgIkNsdXN0ZXJpbmcgY29lZi4iID0gdHJhbnNpdGl2aXR5KGNjbF9wZmFtX2csIHR5cGUgPSAiZ2xvYmFsIiksCiAgIlBvd2VyIGxhdyBjb2VmLiIgPSBmaXRfcG93ZXJfbGF3KGssIHhtaW4gPSAxKSRhbHBoYSwKICAiTWVhbiBzaG9ydGVzdCBwYXRoIiA9IG1lYW4oYmZzX3ZlYywgbmEucm0gPSBUUlVFKSwKICAiRGVuc2l0eSIgPSBlZGdlX2RlbnNpdHkoY2NsX3BmYW1fZykKKQpgYGAKCiMjIyBDb21tdW5pdHkgRGV0ZWN0aW9uIGluIE5ldHdvcmtzIFVzaW5nIEluZm9NYXAgYW5kIFNwaW5HbGFzcwoKYGBge3IsIGZpZy5oZWlnaHQ9MTMuNSwgZmlnLndpZHRoPTEwfQpudW1fdG9wX2NsdXN0cyA8LSAzCgojIEluZm9NYXAgK05ldHdvcmsKY2NsX3BmYW1fcF9nX2ltYXAgPC0gY2x1c3Rlcl9pbmZvbWFwKGNjbF9wZmFtX3BfZykKY2NsX3BmYW1fcF9nX2ltYXBfdG9wX2lkcyA8LSBzb3J0KGFzLm51bWVyaWMobmFtZXMoCiAgc29ydChzaXplcyhjY2xfcGZhbV9wX2dfaW1hcCksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOm51bV90b3BfY2x1c3RzXQopKSkKY2NsX3BmYW1fcF9nX2ltYXBfdG9wX3ZfbXNrIDwtICgKICBjY2xfcGZhbV9wX2dfaW1hcCRtZW1iZXJzaGlwICVpbiUgY2NsX3BmYW1fcF9nX2ltYXBfdG9wX2lkcwopCmNjbF9wZmFtX3BfZ19pbWFwX3RvcF9nIDwtIGluZHVjZWRfc3ViZ3JhcGgoCiAgY2NsX3BmYW1fcF9nLAogIFYoY2NsX3BmYW1fcF9nKVtjY2xfcGZhbV9wX2dfaW1hcF90b3Bfdl9tc2tdCikKIyBpR3JhcGggaGFzIG5vIGZ1bmN0aW9uYWxpdHkgdG8gc3Vic2V0IGNvbW11bml0eSBvYmplY3RzIHNvIGhhY2sgaXQKY2NsX3BmYW1fcF9nX2ltYXBfdG9wIDwtIGNjbF9wZmFtX3BfZ19pbWFwCmNjbF9wZmFtX3BfZ19pbWFwX3RvcCRuYW1lcyA8LQogIGNjbF9wZmFtX3BfZ19pbWFwX3RvcCRuYW1lc1tjY2xfcGZhbV9wX2dfaW1hcF90b3Bfdl9tc2tdCmNjbF9wZmFtX3BfZ19pbWFwX3RvcCRtZW1iZXJzaGlwIDwtCiAgY2NsX3BmYW1fcF9nX2ltYXBfdG9wJG1lbWJlcnNoaXBbY2NsX3BmYW1fcF9nX2ltYXBfdG9wX3ZfbXNrXQpjY2xfcGZhbV9wX2dfaW1hcF90b3AkdmNvdW50IDwtIGxlbmd0aChjY2xfcGZhbV9wX2dfaW1hcF90b3AkbmFtZXMpCgojIFNwaW5HbGFzcyArTmV0d29yawojIFNwaW5HbGFzcyB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fcF9nX3NwaW5fZmlsZSA8LSAiZGF0YS9jY2xfcGZhbV9wX2dfc3Bpbi5SZGEiCmlmICghZmlsZS5leGlzdHMoY2NsX3BmYW1fcF9nX3NwaW5fZmlsZSkpIHsKICBjY2xfcGZhbV9wX2dfc3BpbiA8LSBjbHVzdGVyX3NwaW5nbGFzcyhjY2xfcGZhbV9wX2cpCiAgc2F2ZShjY2xfcGZhbV9wX2dfc3BpbiwgZmlsZSA9IGNjbF9wZmFtX3BfZ19zcGluX2ZpbGUpCn0gZWxzZSB7CiAgbG9hZChjY2xfcGZhbV9wX2dfc3Bpbl9maWxlKQp9CmNjbF9wZmFtX3BfZ19zcGluX3RvcF9pZHMgPC0gc29ydChhcy5udW1lcmljKG5hbWVzKAogIHNvcnQoc2l6ZXMoY2NsX3BmYW1fcF9nX3NwaW4pLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpudW1fdG9wX2NsdXN0c10KKSkpCmNjbF9wZmFtX3BfZ19zcGluX3RvcF92X21zayA8LSAoCiAgY2NsX3BmYW1fcF9nX3NwaW4kbWVtYmVyc2hpcCAlaW4lIGNjbF9wZmFtX3BfZ19zcGluX3RvcF9pZHMKKQpjY2xfcGZhbV9wX2dfc3Bpbl90b3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX3BfZywKICBWKGNjbF9wZmFtX3BfZylbY2NsX3BmYW1fcF9nX3NwaW5fdG9wX3ZfbXNrXQopCiMgaUdyYXBoIGhhcyBubyBmdW5jdGlvbmFsaXR5IHRvIHN1YnNldCBjb21tdW5pdHkgb2JqZWN0cyBzbyBoYWNrIGl0CmNjbF9wZmFtX3BfZ19zcGluX3RvcCA8LSBjY2xfcGZhbV9wX2dfc3BpbgpjY2xfcGZhbV9wX2dfc3Bpbl90b3AkbmFtZXMgPC0KICBjY2xfcGZhbV9wX2dfc3Bpbl90b3AkbmFtZXNbY2NsX3BmYW1fcF9nX3NwaW5fdG9wX3ZfbXNrXQpjY2xfcGZhbV9wX2dfc3Bpbl90b3AkbWVtYmVyc2hpcCA8LQogIGNjbF9wZmFtX3BfZ19zcGluX3RvcCRtZW1iZXJzaGlwW2NjbF9wZmFtX3BfZ19zcGluX3RvcF92X21za10KY2NsX3BmYW1fcF9nX3NwaW5fdG9wJHZjb3VudCA8LSBsZW5ndGgoY2NsX3BmYW1fcF9nX3NwaW5fdG9wJG5hbWVzKQoKIyBTcGluR2xhc3MgKy8tTmV0d29yawojIFNwaW5HbGFzcyB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fZ19zcGluX2ZpbGUgPC0gImRhdGEvY2NsX3BmYW1fZ19zcGluLlJkYSIKaWYgKCFmaWxlLmV4aXN0cyhjY2xfcGZhbV9nX3NwaW5fZmlsZSkpIHsKICBjY2xfcGZhbV9nX3NwaW4gPC0gY2x1c3Rlcl9zcGluZ2xhc3MoY2NsX3BmYW1fZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW1wbGVtZW50YXRpb24gPSAibmVnIikKICBzYXZlKGNjbF9wZmFtX2dfc3BpbiwgZmlsZSA9IGNjbF9wZmFtX2dfc3Bpbl9maWxlKQp9IGVsc2UgewogIGxvYWQoY2NsX3BmYW1fZ19zcGluX2ZpbGUpCn0KY2NsX3BmYW1fZ19zcGluX3RvcF9pZHMgPC0gc29ydChhcy5udW1lcmljKG5hbWVzKAogIHNvcnQoc2l6ZXMoY2NsX3BmYW1fZ19zcGluKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6bnVtX3RvcF9jbHVzdHNdCikpKQpjY2xfcGZhbV9nX3NwaW5fdG9wX3ZfbXNrIDwtICgKICBjY2xfcGZhbV9nX3NwaW4kbWVtYmVyc2hpcCAlaW4lIGNjbF9wZmFtX2dfc3Bpbl90b3BfaWRzCikKY2NsX3BmYW1fZ19zcGluX3RvcF9nIDwtIGluZHVjZWRfc3ViZ3JhcGgoCiAgY2NsX3BmYW1fZywKICBWKGNjbF9wZmFtX2cpW2NjbF9wZmFtX2dfc3Bpbl90b3Bfdl9tc2tdCikKIyBpR3JhcGggaGFzIG5vIGZ1bmN0aW9uYWxpdHkgdG8gc3Vic2V0IGNvbW11bml0eSBvYmplY3RzIHNvIGhhY2sgaXQKY2NsX3BmYW1fZ19zcGluX3RvcCA8LSBjY2xfcGZhbV9nX3NwaW4KY2NsX3BmYW1fZ19zcGluX3RvcCRuYW1lcyA8LQogIGNjbF9wZmFtX2dfc3Bpbl90b3AkbmFtZXNbY2NsX3BmYW1fZ19zcGluX3RvcF92X21za10KY2NsX3BmYW1fZ19zcGluX3RvcCRtZW1iZXJzaGlwIDwtCiAgY2NsX3BmYW1fZ19zcGluX3RvcCRtZW1iZXJzaGlwW2NjbF9wZmFtX2dfc3Bpbl90b3Bfdl9tc2tdCmNjbF9wZmFtX2dfc3Bpbl90b3AkdmNvdW50IDwtIGxlbmd0aChjY2xfcGZhbV9nX3NwaW5fdG9wJG5hbWVzKQoKcGFyKG1mcm93ID0gYygzLCAyKSwgbWFyID0gYygxLCAxLCAzLCAxKSkKcGxvdCgKICBjY2xfcGZhbV9wX2dfaW1hcCwKICBjY2xfcGZhbV9wX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fcF9nX2Nvb3JkLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gIlBGQU0gQ0NMYXNzbyArTmV0d29yayBJbmZvTWFwIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcF9nX2ltYXBfdG9wLAogIGNjbF9wZmFtX3BfZ19pbWFwX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcF9nX2ltYXApW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcF9nX2Nvb3JkW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcyhjY2xfcGZhbV9wX2dfaW1hcCkpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoY2NsX3BmYW1fcF9nX2ltYXApKSwKICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fcF9nX3NwaW4sCiAgY2NsX3BmYW1fcF9nLAogIGxheW91dCA9IGNjbF9wZmFtX3BfZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIENDTGFzc28gK05ldHdvcmsgU3BpbkdsYXNzIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcF9nX3NwaW5fdG9wLAogIGNjbF9wZmFtX3BfZ19zcGluX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcF9nX3NwaW4pW2NjbF9wZmFtX3BfZ19zcGluX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcF9nX2Nvb3JkW2NjbF9wZmFtX3BfZ19zcGluX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcyhjY2xfcGZhbV9wX2dfc3BpbikpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpW2NjbF9wZmFtX3BfZ19zcGluX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoY2NsX3BmYW1fcF9nX3NwaW4pKSwKICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX3BfZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fZ19zcGluLAogIGNjbF9wZmFtX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIENDTGFzc28gKy8tTmV0d29yayBTcGluR2xhc3MgQ2x1c3RlcnMiCikKcGxvdCgKICBjY2xfcGZhbV9nX3NwaW5fdG9wLAogIGNjbF9wZmFtX2dfc3Bpbl90b3BfZywKICBjb2wgPSBtZW1iZXJzaGlwKGNjbF9wZmFtX2dfc3BpbilbY2NsX3BmYW1fZ19zcGluX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fZ19jb29yZFtjY2xfcGZhbV9nX3NwaW5fdG9wX3ZfbXNrLCAsIGRyb3AgPSBGXSwKICBtYXJrLmJvcmRlciA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKGNjbF9wZmFtX2dfc3BpbikpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpW2NjbF9wZmFtX2dfc3Bpbl90b3BfaWRzXSwKICBtYXJrLmNvbCA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKGNjbF9wZmFtX2dfc3BpbikpLAogICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMylbY2NsX3BmYW1fZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KcG5nKAogICJyZXN1bHRzL1BmYW1fQ0NMYXNzb05ldF9DbHVzdGVycy5wbmciLAogIGhlaWdodCA9IDEzLjUsCiAgd2lkdGggPSAxMCwKICB1bml0cyA9ICJpbiIsCiAgcmVzID0gMzAwCikKcGFyKG1mcm93ID0gYygzLCAyKSwgbWFyID0gYygxLCAxLCAzLCAxKSkKcGxvdCgKICBjY2xfcGZhbV9wX2dfaW1hcCwKICBjY2xfcGZhbV9wX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fcF9nX2Nvb3JkLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gIlBGQU0gQ0NMYXNzbyArTmV0d29yayBJbmZvTWFwIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcF9nX2ltYXBfdG9wLAogIGNjbF9wZmFtX3BfZ19pbWFwX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcF9nX2ltYXApW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcF9nX2Nvb3JkW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcyhjY2xfcGZhbV9wX2dfaW1hcCkpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoY2NsX3BmYW1fcF9nX2ltYXApKSwKICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX3BfZ19pbWFwX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fcF9nX3NwaW4sCiAgY2NsX3BmYW1fcF9nLAogIGxheW91dCA9IGNjbF9wZmFtX3BfZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIENDTGFzc28gK05ldHdvcmsgU3BpbkdsYXNzIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcF9nX3NwaW5fdG9wLAogIGNjbF9wZmFtX3BfZ19zcGluX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcF9nX3NwaW4pW2NjbF9wZmFtX3BfZ19zcGluX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcF9nX2Nvb3JkW2NjbF9wZmFtX3BfZ19zcGluX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcyhjY2xfcGZhbV9wX2dfc3BpbikpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpW2NjbF9wZmFtX3BfZ19zcGluX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoY2NsX3BmYW1fcF9nX3NwaW4pKSwKICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX3BfZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fZ19zcGluLAogIGNjbF9wZmFtX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIENDTGFzc28gKy8tTmV0d29yayBTcGluR2xhc3MgQ2x1c3RlcnMiCikKcGxvdCgKICBjY2xfcGZhbV9nX3NwaW5fdG9wLAogIGNjbF9wZmFtX2dfc3Bpbl90b3BfZywKICBjb2wgPSBtZW1iZXJzaGlwKGNjbF9wZmFtX2dfc3BpbilbY2NsX3BmYW1fZ19zcGluX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fZ19jb29yZFtjY2xfcGZhbV9nX3NwaW5fdG9wX3ZfbXNrLCAsIGRyb3AgPSBGXSwKICBtYXJrLmJvcmRlciA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKGNjbF9wZmFtX2dfc3BpbikpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpW2NjbF9wZmFtX2dfc3Bpbl90b3BfaWRzXSwKICBtYXJrLmNvbCA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKGNjbF9wZmFtX2dfc3BpbikpLAogICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMylbY2NsX3BmYW1fZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCmludmlzaWJsZShkZXYub2ZmKCkpCmBgYAoKIyMjIyBTYXZlIGNsdXN0ZXIgcmVzdWx0czoKCmBgYHtyfQpjY2xfcGZhbV9wX2dfaW1hcF9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcF9nX2ltYXAkbWVtYmVyc2hpcCkpIHsKICBjY2xfcGZhbV9wX2dfaW1hcF9kYXRhIDwtIHJiaW5kKAogICAgY2NsX3BmYW1fcF9nX2ltYXBfZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3BfZykkbmFtZVsKICAgICAgICBjY2xfcGZhbV9wX2dfaW1hcCRtZW1iZXJzaGlwID09IGkKICAgICAgXSwKICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICApCiAgKQp9CmNjbF9wZmFtX3BfZ19pbWFwX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3BfZ19pbWFwX2RhdGEsIHBmYW1fZmVhdCwga2V5ID0gIkFjY2Vzc2lvbiIpCndyaXRlLmNzdihjY2xfcGZhbV9wX2dfaW1hcF9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1fQ0NMYXNzb1Bvc05ldF9JbmZvTWFwQ2x1c3RlcnMuY3N2IiwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQpjY2xfcGZhbV9wX2dfc3Bpbl9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcF9nX3NwaW4kbWVtYmVyc2hpcCkpIHsKICBjY2xfcGZhbV9wX2dfc3Bpbl9kYXRhIDwtIHJiaW5kKAogICAgY2NsX3BmYW1fcF9nX3NwaW5fZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3BfZykkbmFtZVsKICAgICAgICBjY2xfcGZhbV9wX2dfc3BpbiRtZW1iZXJzaGlwID09IGkKICAgICAgXSwKICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICApCiAgKQp9CmNjbF9wZmFtX3BfZ19zcGluX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3BfZ19zcGluX2RhdGEsIHBmYW1fZmVhdCwga2V5ID0gIkFjY2Vzc2lvbiIpCndyaXRlLmNzdihjY2xfcGZhbV9wX2dfc3Bpbl9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1fQ0NMYXNzb1Bvc05ldF9TcGluR2xhc3NDbHVzdGVycy5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmNjbF9wZmFtX2dfc3Bpbl9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fZ19zcGluJG1lbWJlcnNoaXApKSB7CiAgY2NsX3BmYW1fZ19zcGluX2RhdGEgPC0gcmJpbmQoCiAgICBjY2xfcGZhbV9nX3NwaW5fZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX2cpJG5hbWVbCiAgICAgICAgY2NsX3BmYW1fZ19zcGluJG1lbWJlcnNoaXAgPT0gaQogICAgICBdLAogICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICkKICApCn0KY2NsX3BmYW1fZ19zcGluX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX2dfc3Bpbl9kYXRhLCBwZmFtX2ZlYXQsIGtleSA9ICJBY2Nlc3Npb24iKQp3cml0ZS5jc3YoY2NsX3BmYW1fZ19zcGluX2RhdGEsCiAgICAgICAgICBmaWxlID0gInJlc3VsdHMvUGZhbV9DQ0xhc3NvTmV0X1NwaW5HbGFzc0NsdXN0ZXJzLmNzdiIsCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgojIyMgQnVpbGQgTmV0d29yayBJbmNsdWRpbmcgUGF0aWVudCBSZXNwb25zZQoKYGBge3J9CiMgQWRkIHBhdGllbnQgcmVzcG9uc2UgdG8gZGF0YXNldApwZmFtX3Jlc3BfZGF0YSA8LSBwZmFtX2RhdGEKcGZhbV9yZXNwX2RhdGEkU2FtcGxlIDwtIHJvd25hbWVzKHBmYW1fcmVzcF9kYXRhKQpwZmFtX3Jlc3BfZGF0YSA8LSBtZXJnZS5lYXN5KHBmYW1fcmVzcF9kYXRhLCBwYXRpZW50X21ldGEsIGtleSA9ICJTYW1wbGUiKQpyb3duYW1lcyhwZmFtX3Jlc3BfZGF0YSkgPC0gcGZhbV9yZXNwX2RhdGEkU2FtcGxlCnBmYW1fcmVzcF9kYXRhJFNhbXBsZSA8LSBOVUxMCgojIENDTGFzc28gdGFrZXMgbWFueSBob3VycyB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fcmVzcF9maWxlIDwtICJkYXRhL2NjbF9wZmFtX3Jlc3BfY3YyMDAuUmRhIgppZiAoIWZpbGUuZXhpc3RzKGNjbF9wZmFtX3Jlc3BfZmlsZSkpIHsKICBjY2xfcGZhbV9yZXNwIDwtIGNjbGFzc28oYXMubWF0cml4KHBmYW1fcmVzcF9kYXRhKSwgY291bnRzID0gVFJVRSkKICBzYXZlKGNjbF9wZmFtX3Jlc3AsIGZpbGUgPSBjY2xfcGZhbV9yZXNwX2ZpbGUpCn0gZWxzZSB7CiAgbG9hZChjY2xfcGZhbV9yZXNwX2ZpbGUpCn0KY2NsX3BmYW1fcmVzcCRjb3Jfd1tpcy5uYW4oY2NsX3BmYW1fcmVzcCRjb3JfdyldIDwtIDAKY2NsX3BmYW1fcmVzcF9wdmFsc192ZWMgPC0gY2NsX3BmYW1fcmVzcCRwX3ZhbHNbdXBwZXIudHJpKGNjbF9wZmFtX3Jlc3AkcF92YWxzKV0KY2NsX3BmYW1fcmVzcF9wdmFsc19hZGogPC0gcC5hZGp1c3QoY2NsX3BmYW1fcmVzcF9wdmFsc192ZWMsICJCSCIpCmNjbF9wZmFtX3Jlc3BfZWRnZXMgPC0gY2NsX3BmYW1fcmVzcCRjb3Jfd1t1cHBlci50cmkoY2NsX3BmYW1fcmVzcCRjb3JfdyldCiMgcC12YWx1ZSA8IDFlLTQgb3RoZXJ3aXNlIENDTGFzc28gbmV0d29yayBudW1iZXIgb2YgZWRnZXMgaXMgdmVyeSBsYXJnZQpjY2xfcGZhbV9yZXNwX2VkZ2VzW2NjbF9wZmFtX3Jlc3BfcHZhbHNfYWRqID4gMWUtNF0gPC0gMApjY2xfcGZhbV9yZXNwX2FtYXQgPC0gbWF0cml4KDAsIGRpbShwZmFtX3Jlc3BfZGF0YSlbMl0sIGRpbShwZmFtX3Jlc3BfZGF0YSlbMl0pCnJvd25hbWVzKGNjbF9wZmFtX3Jlc3BfYW1hdCkgPC0gY29sbmFtZXMocGZhbV9yZXNwX2RhdGEpCmNvbG5hbWVzKGNjbF9wZmFtX3Jlc3BfYW1hdCkgPC0gY29sbmFtZXMocGZhbV9yZXNwX2RhdGEpCmNjbF9wZmFtX3Jlc3BfYW1hdFt1cHBlci50cmkoY2NsX3BmYW1fcmVzcF9hbWF0KV0gPC0gY2NsX3BmYW1fcmVzcF9lZGdlcwpjY2xfcGZhbV9yZXNwX2cgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KAogIGNjbF9wZmFtX3Jlc3BfYW1hdCwKICBtb2RlID0gInVwcGVyIiwKICB3ZWlnaHRlZCA9IFRSVUUsCiAgZGlhZyA9IEZBTFNFCikKY2NsX3BmYW1fcmVzcF9nIDwtIGluZHVjZWRfc3ViZ3JhcGgoCiAgY2NsX3BmYW1fcmVzcF9nLAogIFYoY2NsX3BmYW1fcmVzcF9nKVsKICAgIGNvbXBvbmVudHMoY2NsX3BmYW1fcmVzcF9nKSRtZW1iZXJzaGlwID09CiAgICB3aGljaC5tYXgoY29tcG9uZW50cyhjY2xfcGZhbV9yZXNwX2cpJGNzaXplKQogIF0KKQpjY2xfcGZhbV9yZXNwX3BfZyA8LSBkZWxldGVfZWRnZXMoCiAgY2NsX3BmYW1fcmVzcF9nLAogIEUoY2NsX3BmYW1fcmVzcF9nKVtFKGNjbF9wZmFtX3Jlc3BfZykkd2VpZ2h0IDwgMF0KKQpjY2xfcGZhbV9yZXNwX3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX3Jlc3BfcF9nLAogIFYoY2NsX3BmYW1fcmVzcF9wX2cpWwogICAgY29tcG9uZW50cyhjY2xfcGZhbV9yZXNwX3BfZykkbWVtYmVyc2hpcCA9PQogICAgd2hpY2gubWF4KGNvbXBvbmVudHMoY2NsX3BmYW1fcmVzcF9wX2cpJGNzaXplKQogIF0KKQpjY2xfcGZhbV9yZXNwX3BfZ192X21zayA8LSAoCiAgVihjY2xfcGZhbV9yZXNwX2cpJG5hbWUgJWluJSBWKGNjbF9wZmFtX3Jlc3BfcF9nKSRuYW1lCikKY2NsX3BmYW1fcmVzcF9uX2cgPC0gZGVsZXRlX2VkZ2VzKAogIGNjbF9wZmFtX3Jlc3BfZywKICBFKGNjbF9wZmFtX3Jlc3BfZylbRShjY2xfcGZhbV9yZXNwX2cpJHdlaWdodCA+IDBdCikKY2NsX3BmYW1fcmVzcF9uX2cgPC0gaW5kdWNlZF9zdWJncmFwaCgKICBjY2xfcGZhbV9yZXNwX25fZywKICBWKGNjbF9wZmFtX3Jlc3Bfbl9nKVsKICAgIGNvbXBvbmVudHMoY2NsX3BmYW1fcmVzcF9uX2cpJG1lbWJlcnNoaXAgPT0KICAgIHdoaWNoLm1heChjb21wb25lbnRzKGNjbF9wZmFtX3Jlc3Bfbl9nKSRjc2l6ZSkKICBdCikKY2NsX3BmYW1fcmVzcF9uX2dfdl9tc2sgPC0gKAogIFYoY2NsX3BmYW1fcmVzcF9nKSRuYW1lICVpbiUgVihjY2xfcGZhbV9yZXNwX25fZykkbmFtZQopCmNjbF9wZmFtX3Jlc3BfZ19jb29yZCA8LSBsYXlvdXRfd2l0aF9sZ2woY2NsX3BmYW1fcmVzcF9nKQpjY2xfcGZhbV9yZXNwX3BfZ19jb29yZCA8LQogIGNjbF9wZmFtX3Jlc3BfZ19jb29yZFtjY2xfcGZhbV9yZXNwX3BfZ192X21zaywgLCBkcm9wID0gRl0KY2NsX3BmYW1fcmVzcF9uX2dfY29vcmQgPC0KICBjY2xfcGZhbV9yZXNwX2dfY29vcmRbY2NsX3BmYW1fcmVzcF9uX2dfdl9tc2ssICwgZHJvcCA9IEZdCmBgYAoKIyMjIyBXaGVyZSBkb2VzIHJlc3BvbnNlIGNsdXN0ZXI/CgpgYGB7ciwgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9MTB9CmNjbF9wZmFtX3Jlc3BfcF9nX2ltYXAgPC0gY2x1c3Rlcl9pbmZvbWFwKGNjbF9wZmFtX3Jlc3BfcF9nKQojIFNwaW5HbGFzcyB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9maWxlIDwtICJkYXRhL2NjbF9wZmFtX3Jlc3BfcF9nX3NwaW4uUmRhIgppZiAoIWZpbGUuZXhpc3RzKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fZmlsZSkpIHsKICBjY2xfcGZhbV9yZXNwX3BfZ19zcGluIDwtIGNsdXN0ZXJfc3BpbmdsYXNzKGNjbF9wZmFtX3Jlc3BfcF9nKQogIHNhdmUoY2NsX3BmYW1fcmVzcF9wX2dfc3BpbiwgZmlsZSA9IGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fZmlsZSkKfSBlbHNlIHsKICBsb2FkKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fZmlsZSkKfQojIFNwaW5HbGFzcyB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fcmVzcF9nX3NwaW5fZmlsZSA8LSAiZGF0YS9jY2xfcGZhbV9yZXNwX2dfc3Bpbi5SZGEiCmlmICghZmlsZS5leGlzdHMoY2NsX3BmYW1fcmVzcF9nX3NwaW5fZmlsZSkpIHsKICBjY2xfcGZhbV9yZXNwX2dfc3BpbiA8LSBjbHVzdGVyX3NwaW5nbGFzcyhjY2xfcGZhbV9yZXNwX2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW1wbGVtZW50YXRpb24gPSAibmVnIikKICBzYXZlKGNjbF9wZmFtX3Jlc3BfZ19zcGluLCBmaWxlID0gY2NsX3BmYW1fcmVzcF9nX3NwaW5fZmlsZSkKfSBlbHNlIHsKICBsb2FkKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2ZpbGUpCn0KY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcCRtZW1iZXJzaGlwKSkgewogIGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfZGF0YSA8LSByYmluZCgKICAgIGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3Jlc3BfcF9nKSRuYW1lWwogICAgICAgIGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXAkbWVtYmVyc2hpcCA9PSBpCiAgICAgIF0sCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgKQogICkKfQpjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfZGF0YSwgcGZhbV9mZWF0LCBrZXkgPSAiQWNjZXNzaW9uIikKY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcmVzcF9wX2dfc3BpbiRtZW1iZXJzaGlwKSkgewogIGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fZGF0YSA8LSByYmluZCgKICAgIGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3Jlc3BfcF9nKSRuYW1lWwogICAgICAgIGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW4kbWVtYmVyc2hpcCA9PSBpCiAgICAgIF0sCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgKQogICkKfQpjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fZGF0YSwgcGZhbV9mZWF0LCBrZXkgPSAiQWNjZXNzaW9uIikKY2NsX3BmYW1fcmVzcF9nX3NwaW5fZGF0YSA8LSBkYXRhLmZyYW1lKCkKZm9yIChpIGluIDE6bWF4KGNjbF9wZmFtX3Jlc3BfZ19zcGluJG1lbWJlcnNoaXApKSB7CiAgY2NsX3BmYW1fcmVzcF9nX3NwaW5fZGF0YSA8LSByYmluZCgKICAgIGNjbF9wZmFtX3Jlc3BfZ19zcGluX2RhdGEsCiAgICBkYXRhLmZyYW1lKAogICAgICBDbHVzdGVyID0gaSwKICAgICAgQWNjZXNzaW9uID0gVihjY2xfcGZhbV9yZXNwX2cpJG5hbWVbCiAgICAgICAgY2NsX3BmYW1fcmVzcF9nX3NwaW4kbWVtYmVyc2hpcCA9PSBpCiAgICAgIF0sCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgKQogICkKfQpjY2xfcGZhbV9yZXNwX2dfc3Bpbl9kYXRhIDwtCiAgbWVyZ2UuZWFzeShjY2xfcGZhbV9yZXNwX2dfc3Bpbl9kYXRhLCBwZmFtX2ZlYXQsIGtleSA9ICJBY2Nlc3Npb24iKQp3cml0ZS5jc3YoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1SZXNfQ0NMYXNzb1Bvc05ldF9JbmZvTWFwQ2x1c3RlcnMuY3N2IiwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1SZXNfQ0NMYXNzb1Bvc05ldF9TcGluR2xhc3NDbHVzdGVycy5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihjY2xfcGZhbV9yZXNwX2dfc3Bpbl9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1SZXNfQ0NMYXNzb05ldF9TcGluR2xhc3NDbHVzdGVycy5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmNhdCgKICAiSW5mb01hcCArTmV0d29yayByZXNwb25zZSBjbHVzdGVyOiIsCiAgY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9kYXRhJENsdXN0ZXJbCiAgICBjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2RhdGEkQWNjZXNzaW9uID09ICJSZXNwb25zZUJpbmFyeSIKICBdLAogICJcblNwaW5HbGFzcyArTmV0d29yayByZXNwb25zZSBjbHVzdGVyOiIsCiAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9kYXRhJENsdXN0ZXJbCiAgICBjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2RhdGEkQWNjZXNzaW9uID09ICJSZXNwb25zZUJpbmFyeSIKICBdLAogICJcblNwaW5HbGFzcyArLy1OZXR3b3JrIHJlc3BvbnNlIGNsdXN0ZXI6IiwKICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9kYXRhJENsdXN0ZXJbCiAgICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9kYXRhJEFjY2Vzc2lvbiA9PSAiUmVzcG9uc2VCaW5hcnkiCiAgXSwKICAiXG4iCikKYGBgCgojIyMgQmlwYXJ0aXRlIEdyYXBocyBvZiBJbmZvTWFwIGFuZCBTcGluR2xhc3MgQ2x1c3RlcnMgRWZmZWN0IG9uIFJlc3BvbnNlCgpgYGB7ciwgZmlnLmhlaWdodD00LjUsIGZpZy53aWR0aD0xMH0KY2NsX3BmYW1fcF9nX2ltYXBfY2x1c3RzIDwtIGRhdGEuZnJhbWUoCiAgTm9kZSA9IFYoY2NsX3BmYW1fcF9nKSRuYW1lLAogIENsdXN0ZXIgPSBjY2xfcGZhbV9wX2dfaW1hcCRtZW1iZXJzaGlwLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCmNjbF9wZmFtX3BfZ19zcGluX2NsdXN0cyA8LSBkYXRhLmZyYW1lKAogIE5vZGUgPSBWKGNjbF9wZmFtX3BfZykkbmFtZSwKICBDbHVzdGVyID0gY2NsX3BmYW1fcF9nX3NwaW4kbWVtYmVyc2hpcCwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQpjY2xfcGZhbV9nX3NwaW5fY2x1c3RzIDwtIGRhdGEuZnJhbWUoCiAgTm9kZSA9IFYoY2NsX3BmYW1fZykkbmFtZSwKICBDbHVzdGVyID0gY2NsX3BmYW1fZ19zcGluJG1lbWJlcnNoaXAsCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCmNjbF9wZmFtX3Jlc3BfZ19lZGdlcyA8LSBjYmluZChhc19lZGdlbGlzdChjY2xfcGZhbV9yZXNwX2cpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRShjY2xfcGZhbV9yZXNwX2cpJHdlaWdodCkKY2NsX3BmYW1fcmVzcF9nX3Jlc3BfZWRnZXMgPC0KICBhcy5kYXRhLmZyYW1lKGNjbF9wZmFtX3Jlc3BfZ19lZGdlc1tjKAogICAgY2NsX3BmYW1fcmVzcF9nX2VkZ2VzWywgMV0gPT0gIlJlc3BvbnNlQmluYXJ5IiB8CiAgICAgIGNjbF9wZmFtX3Jlc3BfZ19lZGdlc1ssIDJdID09ICJSZXNwb25zZUJpbmFyeSIKICApLCBdLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCm5hbWVzKGNjbF9wZmFtX3Jlc3BfZ19yZXNwX2VkZ2VzKSA8LSBjKCJOb2RlIiwgIlNpdGUiLCAiV2VpZ2h0IikKCmNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfY2x1c3RzX3Jlc3BfZWRnZXMgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3Jlc3BfZ19yZXNwX2VkZ2VzLAogICAgICAgICAgICAgY2NsX3BmYW1fcF9nX2ltYXBfY2x1c3RzLAogICAgICAgICAgICAga2V5ID0gIk5vZGUiKQpjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2NsdXN0c19yZXNwX2VkZ2VzJFdlaWdodCA8LQogIGFzLm51bWVyaWMoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9jbHVzdHNfcmVzcF9lZGdlcyRXZWlnaHQpCmNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fY2x1c3RzX3Jlc3BfZWRnZXMgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3Jlc3BfZ19yZXNwX2VkZ2VzLAogICAgICAgICAgICAgY2NsX3BmYW1fcF9nX3NwaW5fY2x1c3RzLAogICAgICAgICAgICAga2V5ID0gIk5vZGUiKQpjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2NsdXN0c19yZXNwX2VkZ2VzJFdlaWdodCA8LQogIGFzLm51bWVyaWMoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9jbHVzdHNfcmVzcF9lZGdlcyRXZWlnaHQpCmNjbF9wZmFtX3Jlc3BfZ19zcGluX2NsdXN0c19yZXNwX2VkZ2VzIDwtCiAgbWVyZ2UuZWFzeShjY2xfcGZhbV9yZXNwX2dfcmVzcF9lZGdlcywKICAgICAgICAgICAgIGNjbF9wZmFtX2dfc3Bpbl9jbHVzdHMsCiAgICAgICAgICAgICBrZXkgPSAiTm9kZSIpCmNjbF9wZmFtX3Jlc3BfZ19zcGluX2NsdXN0c19yZXNwX2VkZ2VzJFdlaWdodCA8LQogIGFzLm51bWVyaWMoY2NsX3BmYW1fcmVzcF9nX3NwaW5fY2x1c3RzX3Jlc3BfZWRnZXMkV2VpZ2h0KQoKY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9jbHVzdHNfcmVzcF9jbHVzdHMgPC0KICBjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2NsdXN0c19yZXNwX2VkZ2VzICU+JQogIGdyb3VwX2J5KFNpdGUsIENsdXN0ZXIpICU+JQogIHN1bW1hcmlzZShXZWlnaHQgPSBtZWFuKFdlaWdodCkpICU+JQogIHN1YnNldCghaXMubmEoQ2x1c3RlcikpCmNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fY2x1c3RzX3Jlc3BfY2x1c3RzIDwtCiAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9jbHVzdHNfcmVzcF9lZGdlcyAlPiUKICBncm91cF9ieShTaXRlLCBDbHVzdGVyKSAlPiUKICBzdW1tYXJpc2UoV2VpZ2h0ID0gbWVhbihXZWlnaHQpKSAlPiUKICBzdWJzZXQoIWlzLm5hKENsdXN0ZXIpKQpjY2xfcGZhbV9yZXNwX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdHMgPC0KICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9jbHVzdHNfcmVzcF9lZGdlcyAlPiUKICBncm91cF9ieShTaXRlLCBDbHVzdGVyKSAlPiUKICBzdW1tYXJpc2UoV2VpZ2h0ID0gbWVhbihXZWlnaHQpKSAlPiUKICBzdWJzZXQoIWlzLm5hKENsdXN0ZXIpKQoKY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZyA8LQogIGdyYXBoLmRhdGEuZnJhbWUoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9jbHVzdHNfcmVzcF9jbHVzdHNbLCAxOjJdLAogICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGQUxTRSkKVihjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSR0eXBlIDwtCiAgVihjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSRuYW1lICVpbiUKICBhcy5jaGFyYWN0ZXIodW5saXN0KGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfY2x1c3RzX3Jlc3BfY2x1c3RzWywgMV0pKQpFKGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfYmlwYXJ0X2cpJHdlaWdodCA8LQogIGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfY2x1c3RzX3Jlc3BfY2x1c3RzJFdlaWdodApWKGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfYmlwYXJ0X2cpJGNvbG9yIDwtCiAgVihjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSR0eXBlClYoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZykkY29sb3IgPC0KICBnc3ViKCJGQUxTRSIsIHJnYigwLCAxLCAxLCAwLjIpLAogICAgICAgVihjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSRjb2xvcikKVihjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSRjb2xvciA8LQogIGdzdWIoIlRSVUUiLCByZ2IoMSwgMSwgMCwgMC4yKSwKICAgICAgIFYoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZykkY29sb3IpCgpjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2JpcGFydF9nIDwtCiAgZ3JhcGguZGF0YS5mcmFtZShjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2NsdXN0c19yZXNwX2NsdXN0c1ssIDE6Ml0sCiAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IEZBTFNFKQpWKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fYmlwYXJ0X2cpJHR5cGUgPC0KICBWKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fYmlwYXJ0X2cpJG5hbWUgJWluJQogIGFzLmNoYXJhY3Rlcih1bmxpc3QoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdHNbLCAxXSkpCkUoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9iaXBhcnRfZykkd2VpZ2h0IDwtCiAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdHMkV2VpZ2h0ClYoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9iaXBhcnRfZykkY29sb3IgPC0KICBWKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fYmlwYXJ0X2cpJHR5cGUKVihjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2JpcGFydF9nKSRjb2xvciA8LQogIGdzdWIoIkZBTFNFIiwgcmdiKDAsIDEsIDEsIDAuMiksCiAgICAgICBWKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fYmlwYXJ0X2cpJGNvbG9yKQpWKGNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fYmlwYXJ0X2cpJGNvbG9yIDwtCiAgZ3N1YigiVFJVRSIsIHJnYigxLCAxLCAwLCAwLjIpLAogICAgICAgVihjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2JpcGFydF9nKSRjb2xvcikKCmNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nIDwtCiAgZ3JhcGguZGF0YS5mcmFtZShjY2xfcGZhbV9yZXNwX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdHNbLCAxOjJdLAogICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGQUxTRSkKVihjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZykkdHlwZSA8LQogIFYoY2NsX3BmYW1fcmVzcF9nX3NwaW5fYmlwYXJ0X2cpJG5hbWUgJWluJQogIGFzLmNoYXJhY3Rlcih1bmxpc3QoY2NsX3BmYW1fcmVzcF9nX3NwaW5fY2x1c3RzX3Jlc3BfY2x1c3RzWywgMV0pKQpFKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nKSR3ZWlnaHQgPC0KICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdHMkV2VpZ2h0ClYoY2NsX3BmYW1fcmVzcF9nX3NwaW5fYmlwYXJ0X2cpJGNvbG9yIDwtCiAgVihjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZykkdHlwZQpWKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nKSRjb2xvciA8LQogIGdzdWIoIkZBTFNFIiwgcmdiKDAsIDEsIDEsIDAuMiksCiAgICAgICBWKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nKSRjb2xvcikKVihjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZykkY29sb3IgPC0KICBnc3ViKCJUUlVFIiwgcmdiKDEsIDEsIDAsIDAuMiksCiAgICAgICBWKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nKSRjb2xvcikKCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoMSwgMSwgMywgMSkpCnBsb3QoCiAgY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZywKICBlZGdlLmNvbG9yID0gaWZlbHNlKAogICAgRShjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSR3ZWlnaHQgPiAwLAogICAgcmdiKDAsIDEsIDApLAogICAgcmdiKDEsIDAsIDApCiAgKSwKICBlZGdlLndpZHRoID0gYWJzKEUoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZykkd2VpZ2h0KSAqIDMwLAogIGxheW91dCA9IGxheW91dF9hc19iaXBhcnRpdGUsCiAgdmVydGV4LnNpemUgPSAxNSwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMSwKICB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siLAogIG1haW4gPSAiUEZBTSBSZXNwb25zZSBDQ0xhc3NvICtOZXR3b3JrXG5JbmZvTWFwIENsdXN0ZXIgRWZmZWN0IG9uIFJlc3BvbnNlIgopCnBsb3QoCiAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9iaXBhcnRfZywKICBlZGdlLmNvbG9yID0gaWZlbHNlKAogICAgRShjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2JpcGFydF9nKSR3ZWlnaHQgPiAwLAogICAgcmdiKDAsIDEsIDApLAogICAgcmdiKDEsIDAsIDApCiAgKSwKICBlZGdlLndpZHRoID0gYWJzKEUoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9iaXBhcnRfZykkd2VpZ2h0KSAqIDMwLAogIGxheW91dCA9IGxheW91dF9hc19iaXBhcnRpdGUsCiAgdmVydGV4LnNpemUgPSAxNSwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMSwKICB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siLAogIG1haW4gPSAiUEZBTSBSZXNwb25zZSBDQ0xhc3NvICtOZXR3b3JrXG5TcGluR2xhc3MgQ2x1c3RlciBFZmZlY3Qgb24gUmVzcG9uc2UiCikKcGxvdCgKICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZywKICBlZGdlLmNvbG9yID0gaWZlbHNlKAogICAgRShjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZykkd2VpZ2h0ID4gMCwKICAgIHJnYigwLCAxLCAwKSwKICAgIHJnYigxLCAwLCAwKQogICksCiAgZWRnZS53aWR0aCA9IGFicyhFKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nKSR3ZWlnaHQpICogMzAsCiAgbGF5b3V0ID0gbGF5b3V0X2FzX2JpcGFydGl0ZSwKICB2ZXJ0ZXguc2l6ZSA9IDE1LAogIHZlcnRleC5sYWJlbC5jZXggPSAxLAogIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIsCiAgbWFpbiA9ICJQRkFNIFJlc3BvbnNlIENDTGFzc28gKy8tTmV0d29ya1xuU3BpbkdsYXNzIENsdXN0ZXIgRWZmZWN0IG9uIFJlc3BvbnNlIgopCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KcG5nKAogICJyZXN1bHRzL1BmYW1SZXNfQ0NMYXNzb05ldF9CaXBhcnRpdGVDbHVzdGVyaW5nLnBuZyIsCiAgaGVpZ2h0ID0gNC41LAogIHdpZHRoID0gMTAsCiAgdW5pdHMgPSAiaW4iLAogIHJlcyA9IDMwMAopCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoMSwgMSwgMywgMSkpCnBsb3QoCiAgY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZywKICBlZGdlLmNvbG9yID0gaWZlbHNlKAogICAgRShjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2JpcGFydF9nKSR3ZWlnaHQgPiAwLAogICAgcmdiKDAsIDEsIDApLAogICAgcmdiKDEsIDAsIDApCiAgKSwKICBlZGdlLndpZHRoID0gYWJzKEUoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9iaXBhcnRfZykkd2VpZ2h0KSAqIDMwLAogIGxheW91dCA9IGxheW91dF9hc19iaXBhcnRpdGUsCiAgdmVydGV4LnNpemUgPSAxNSwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMSwKICB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siLAogIG1haW4gPSAiUEZBTSBSZXNwb25zZSBDQ0xhc3NvICtOZXR3b3JrXG5JbmZvTWFwIENsdXN0ZXIgRWZmZWN0IG9uIFJlc3BvbnNlIgopCnBsb3QoCiAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9iaXBhcnRfZywKICBlZGdlLmNvbG9yID0gaWZlbHNlKAogICAgRShjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2JpcGFydF9nKSR3ZWlnaHQgPiAwLAogICAgcmdiKDAsIDEsIDApLAogICAgcmdiKDEsIDAsIDApCiAgKSwKICBlZGdlLndpZHRoID0gYWJzKEUoY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9iaXBhcnRfZykkd2VpZ2h0KSAqIDMwLAogIGxheW91dCA9IGxheW91dF9hc19iaXBhcnRpdGUsCiAgdmVydGV4LnNpemUgPSAxNSwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMSwKICB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siLAogIG1haW4gPSAiUEZBTSBSZXNwb25zZSBDQ0xhc3NvICtOZXR3b3JrXG5TcGluR2xhc3MgQ2x1c3RlciBFZmZlY3Qgb24gUmVzcG9uc2UiCikKcGxvdCgKICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZywKICBlZGdlLmNvbG9yID0gaWZlbHNlKAogICAgRShjY2xfcGZhbV9yZXNwX2dfc3Bpbl9iaXBhcnRfZykkd2VpZ2h0ID4gMCwKICAgIHJnYigwLCAxLCAwKSwKICAgIHJnYigxLCAwLCAwKQogICksCiAgZWRnZS53aWR0aCA9IGFicyhFKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2JpcGFydF9nKSR3ZWlnaHQpICogMzAsCiAgbGF5b3V0ID0gbGF5b3V0X2FzX2JpcGFydGl0ZSwKICB2ZXJ0ZXguc2l6ZSA9IDE1LAogIHZlcnRleC5sYWJlbC5jZXggPSAxLAogIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIsCiAgbWFpbiA9ICJQRkFNIFJlc3BvbnNlIENDTGFzc28gKy8tTmV0d29ya1xuU3BpbkdsYXNzIENsdXN0ZXIgRWZmZWN0IG9uIFJlc3BvbnNlIgopCmludmlzaWJsZShkZXYub2ZmKCkpCmBgYAoKIyMjIyBTYXZlIGNsdXN0ZXIgYmlwYXJ0aXRlIHJlc3VsdHM6CgpgYGB7cn0KcGZhbV9mZWF0XzIgPC0gcGZhbV9mZWF0Cm5hbWVzKHBmYW1fZmVhdF8yKVsxXSA8LSAiTm9kZSIKCmNjbF9wZmFtX3BfZ19pbWFwX2NsdXN0cyA8LQogIG1lcmdlLmVhc3koY2NsX3BmYW1fcF9nX2ltYXBfY2x1c3RzLCBwZmFtX2ZlYXRfMiwga2V5ID0gIk5vZGUiKQpjY2xfcGZhbV9yZXNwX3BfZ19pbWFwX2NsdXN0c19yZXNwX2NsdXN0X3dlaWdodCA8LQogIGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfY2x1c3RzX3Jlc3BfY2x1c3RzWywgYygiQ2x1c3RlciIsICJXZWlnaHQiKV0KbmFtZXMoY2NsX3BmYW1fcmVzcF9wX2dfaW1hcF9jbHVzdHNfcmVzcF9jbHVzdF93ZWlnaHQpWzJdIDwtICJSZXNwb25zZUVmZmVjdCIKY2NsX3BmYW1fcF9nX2ltYXBfY2x1c3RzIDwtCiAgbWVyZ2UuZWFzeSgKICAgIGNjbF9wZmFtX3BfZ19pbWFwX2NsdXN0cywKICAgIGNjbF9wZmFtX3Jlc3BfcF9nX2ltYXBfY2x1c3RzX3Jlc3BfY2x1c3Rfd2VpZ2h0LAogICAga2V5ID0gIkNsdXN0ZXIiCiAgKQp3cml0ZS5jc3YoCiAgY2NsX3BmYW1fcF9nX2ltYXBfY2x1c3RzLAogIGZpbGUgPSAicmVzdWx0cy9QZmFtX0NDTGFzc29Qb3NOZXRfSW5mb01hcENsdXN0ZXJzX1Jlc3BvbnNlRWZmZWN0LmNzdiIsCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKY2NsX3BmYW1fcF9nX3NwaW5fY2x1c3RzIDwtCiAgbWVyZ2UuZWFzeShjY2xfcGZhbV9wX2dfc3Bpbl9jbHVzdHMsIHBmYW1fZmVhdF8yLCBrZXkgPSAiTm9kZSIpCmNjbF9wZmFtX3Jlc3BfcF9nX3NwaW5fY2x1c3RzX3Jlc3BfY2x1c3Rfd2VpZ2h0IDwtCiAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdHNbLCBjKCJDbHVzdGVyIiwgIldlaWdodCIpXQpuYW1lcyhjY2xfcGZhbV9yZXNwX3BfZ19zcGluX2NsdXN0c19yZXNwX2NsdXN0X3dlaWdodClbMl0gPC0gIlJlc3BvbnNlRWZmZWN0IgpjY2xfcGZhbV9wX2dfc3Bpbl9jbHVzdHMgPC0KICBtZXJnZS5lYXN5KAogICAgY2NsX3BmYW1fcF9nX3NwaW5fY2x1c3RzLAogICAgY2NsX3BmYW1fcmVzcF9wX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdF93ZWlnaHQsCiAgICBrZXkgPSAiQ2x1c3RlciIKICApCndyaXRlLmNzdigKICBjY2xfcGZhbV9wX2dfc3Bpbl9jbHVzdHMsCiAgZmlsZSA9ICJyZXN1bHRzL1BmYW1fQ0NMYXNzb1Bvc05ldF9TcGluR2xhc3NDbHVzdGVyc19SZXNwb25zZUVmZmVjdC5jc3YiLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKCmNjbF9wZmFtX2dfc3Bpbl9jbHVzdHMgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX2dfc3Bpbl9jbHVzdHMsIHBmYW1fZmVhdF8yLCBrZXkgPSAiTm9kZSIpCmNjbF9wZmFtX3Jlc3BfZ19zcGluX2NsdXN0c19yZXNwX2NsdXN0X3dlaWdodCA8LQogIGNjbF9wZmFtX3Jlc3BfZ19zcGluX2NsdXN0c19yZXNwX2NsdXN0c1ssIGMoIkNsdXN0ZXIiLCAiV2VpZ2h0IildCm5hbWVzKGNjbF9wZmFtX3Jlc3BfZ19zcGluX2NsdXN0c19yZXNwX2NsdXN0X3dlaWdodClbMl0gPC0gIlJlc3BvbnNlRWZmZWN0IgpjY2xfcGZhbV9nX3NwaW5fY2x1c3RzIDwtCiAgbWVyZ2UuZWFzeSgKICAgIGNjbF9wZmFtX2dfc3Bpbl9jbHVzdHMsCiAgICBjY2xfcGZhbV9yZXNwX2dfc3Bpbl9jbHVzdHNfcmVzcF9jbHVzdF93ZWlnaHQsCiAgICBrZXkgPSAiQ2x1c3RlciIKICApCndyaXRlLmNzdigKICBjY2xfcGZhbV9nX3NwaW5fY2x1c3RzLAogIGZpbGUgPSAicmVzdWx0cy9QZmFtX0NDTGFzc29OZXRfU3BpbkdsYXNzQ2x1c3RlcnNfUmVzcG9uc2VFZmZlY3QuY3N2IiwKICByb3cubmFtZXMgPSBGQUxTRQopCmBgYAoKIyMjIEJ1aWxkIFJlc3BvbmRlciBhbmQgTm9uLVJlc3BvbmRlciBOZXR3b3JrcwoKYGBge3J9CiMgUmVzcG9uZGVyCnBmYW1fcnNwZHJfZGF0YSA8LQogIHBmYW1fZGF0YVtwYXRpZW50X21ldGEkU2FtcGxlW3BhdGllbnRfbWV0YSRSZXNwb25zZUJpbmFyeSA9PSAxXSwgXQoKIyBDQ0xhc3NvIHRha2VzIG1hbnkgaG91cnMgdG8gcnVuIChsb2FkIGNhY2hlZCBpZiBhdmFpbGFibGUpCmNjbF9wZmFtX3JzcGRyX2ZpbGUgPC0gImRhdGEvY2NsX3BmYW1fcnNwZHJfY3YyMDAuUmRhIgppZiAoIWZpbGUuZXhpc3RzKGNjbF9wZmFtX3JzcGRyX2ZpbGUpKSB7CiAgY2NsX3BmYW1fcnNwZHIgPC0gY2NsYXNzbyhhcy5tYXRyaXgocGZhbV9yc3Bkcl9kYXRhKSwgY291bnRzID0gVFJVRSkKICBzYXZlKGNjbF9wZmFtX3JzcGRyLCBmaWxlID0gY2NsX3BmYW1fcnNwZHJfZmlsZSkKfSBlbHNlIHsKICBsb2FkKGNjbF9wZmFtX3JzcGRyX2ZpbGUpCn0KY2NsX3BmYW1fcnNwZHIkY29yX3dbaXMubmFuKGNjbF9wZmFtX3JzcGRyJGNvcl93KV0gPC0gMApjY2xfcGZhbV9yc3Bkcl9wdmFsc192ZWMgPC0KICBjY2xfcGZhbV9yc3BkciRwX3ZhbHNbdXBwZXIudHJpKGNjbF9wZmFtX3JzcGRyJHBfdmFscyldCmNjbF9wZmFtX3JzcGRyX3B2YWxzX2FkaiA8LQogIHAuYWRqdXN0KGNjbF9wZmFtX3JzcGRyX3B2YWxzX3ZlYywgIkJIIikKY2NsX3BmYW1fcnNwZHJfZWRnZXMgPC0KICBjY2xfcGZhbV9yc3BkciRjb3Jfd1t1cHBlci50cmkoY2NsX3BmYW1fcnNwZHIkY29yX3cpXQojIHAtdmFsdWUgPCAxZS00IG90aGVyd2lzZSBDQ0xhc3NvIG5ldHdvcmsgbnVtYmVyIG9mIGVkZ2VzIGlzIHZlcnkgbGFyZ2UKY2NsX3BmYW1fcnNwZHJfZWRnZXNbY2NsX3BmYW1fcnNwZHJfcHZhbHNfYWRqID4gMWUtNF0gPC0gMApjY2xfcGZhbV9yc3Bkcl9hbWF0IDwtCiAgbWF0cml4KDAsIGRpbShwZmFtX3JzcGRyX2RhdGEpWzJdLCBkaW0ocGZhbV9yc3Bkcl9kYXRhKVsyXSkKcm93bmFtZXMoY2NsX3BmYW1fcnNwZHJfYW1hdCkgPC0gY29sbmFtZXMocGZhbV9yc3Bkcl9kYXRhKQpjb2xuYW1lcyhjY2xfcGZhbV9yc3Bkcl9hbWF0KSA8LSBjb2xuYW1lcyhwZmFtX3JzcGRyX2RhdGEpCmNjbF9wZmFtX3JzcGRyX2FtYXRbdXBwZXIudHJpKGNjbF9wZmFtX3JzcGRyX2FtYXQpXSA8LSBjY2xfcGZhbV9yc3Bkcl9lZGdlcwpjY2xfcGZhbV9yc3Bkcl9nIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCgKICBjY2xfcGZhbV9yc3Bkcl9hbWF0LAogIG1vZGUgPSAidXBwZXIiLAogIHdlaWdodGVkID0gVFJVRSwKICBkaWFnID0gRkFMU0UKKQpjY2xfcGZhbV9yc3Bkcl9nIDwtIGluZHVjZWRfc3ViZ3JhcGgoCiAgY2NsX3BmYW1fcnNwZHJfZywKICBWKGNjbF9wZmFtX3JzcGRyX2cpWwogICAgY29tcG9uZW50cyhjY2xfcGZhbV9yc3Bkcl9nKSRtZW1iZXJzaGlwID09CiAgICB3aGljaC5tYXgoY29tcG9uZW50cyhjY2xfcGZhbV9yc3Bkcl9nKSRjc2l6ZSkKICBdCikKY2NsX3BmYW1fcnNwZHJfZ192X21zayA8LSAoCiAgVihjY2xfcGZhbV9yc3Bkcl9nKSRuYW1lICVpbiUgVihjY2xfcGZhbV9yc3Bkcl9nKSRuYW1lCikKY2NsX3BmYW1fcnNwZHJfcF9nIDwtIGRlbGV0ZV9lZGdlcygKICBjY2xfcGZhbV9yc3Bkcl9nLAogIEUoY2NsX3BmYW1fcnNwZHJfZylbRShjY2xfcGZhbV9yc3Bkcl9nKSR3ZWlnaHQgPCAwXQopCmNjbF9wZmFtX3JzcGRyX3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX3JzcGRyX3BfZywKICBWKGNjbF9wZmFtX3JzcGRyX3BfZylbCiAgICBjb21wb25lbnRzKGNjbF9wZmFtX3JzcGRyX3BfZykkbWVtYmVyc2hpcCA9PQogICAgd2hpY2gubWF4KGNvbXBvbmVudHMoY2NsX3BmYW1fcnNwZHJfcF9nKSRjc2l6ZSkKICBdCikKY2NsX3BmYW1fcnNwZHJfcF9nX3ZfbXNrIDwtICgKICBWKGNjbF9wZmFtX3JzcGRyX2cpJG5hbWUgJWluJSBWKGNjbF9wZmFtX3JzcGRyX3BfZykkbmFtZQopCmNjbF9wZmFtX3JzcGRyX25fZyA8LSBkZWxldGVfZWRnZXMoCiAgY2NsX3BmYW1fcnNwZHJfZywKICBFKGNjbF9wZmFtX3JzcGRyX2cpW0UoY2NsX3BmYW1fcnNwZHJfZykkd2VpZ2h0ID4gMF0KKQpjY2xfcGZhbV9yc3Bkcl9uX2cgPC0gaW5kdWNlZF9zdWJncmFwaCgKICBjY2xfcGZhbV9yc3Bkcl9uX2csCiAgVihjY2xfcGZhbV9yc3Bkcl9uX2cpWwogICAgY29tcG9uZW50cyhjY2xfcGZhbV9yc3Bkcl9uX2cpJG1lbWJlcnNoaXAgPT0KICAgIHdoaWNoLm1heChjb21wb25lbnRzKGNjbF9wZmFtX3JzcGRyX25fZykkY3NpemUpCiAgXQopCmNjbF9wZmFtX3JzcGRyX25fZ192X21zayA8LSAoCiAgVihjY2xfcGZhbV9yc3Bkcl9nKSRuYW1lICVpbiUgVihjY2xfcGZhbV9yc3Bkcl9uX2cpJG5hbWUKKQoKIyBOb24tUmVzcG9uZGVyCnBmYW1fbnNwZHJfZGF0YSA8LQogIHBmYW1fZGF0YVtwYXRpZW50X21ldGEkU2FtcGxlW3BhdGllbnRfbWV0YSRSZXNwb25zZUJpbmFyeSA9PSAwXSwgXQoKIyBDQ0xhc3NvIHRha2VzIG1hbnkgaG91cnMgdG8gcnVuIChsb2FkIGNhY2hlZCBpZiBhdmFpbGFibGUpCmNjbF9wZmFtX25zcGRyX2ZpbGUgPC0gImRhdGEvY2NsX3BmYW1fbnNwZHJfY3YyMDAuUmRhIgppZiAoIWZpbGUuZXhpc3RzKGNjbF9wZmFtX25zcGRyX2ZpbGUpKSB7CiAgY2NsX3BmYW1fbnNwZHIgPC0gY2NsYXNzbyhhcy5tYXRyaXgocGZhbV9uc3Bkcl9kYXRhKSwgY291bnRzID0gVFJVRSkKICBzYXZlKGNjbF9wZmFtX25zcGRyLCBmaWxlID0gY2NsX3BmYW1fbnNwZHJfZmlsZSkKfSBlbHNlIHsKICBsb2FkKGNjbF9wZmFtX25zcGRyX2ZpbGUpCn0KY2NsX3BmYW1fbnNwZHIkY29yX3dbaXMubmFuKGNjbF9wZmFtX25zcGRyJGNvcl93KV0gPC0gMApjY2xfcGZhbV9uc3Bkcl9wdmFsc192ZWMgPC0KICBjY2xfcGZhbV9uc3BkciRwX3ZhbHNbdXBwZXIudHJpKGNjbF9wZmFtX25zcGRyJHBfdmFscyldCmNjbF9wZmFtX25zcGRyX3B2YWxzX2FkaiA8LQogIHAuYWRqdXN0KGNjbF9wZmFtX25zcGRyX3B2YWxzX3ZlYywgIkJIIikKY2NsX3BmYW1fbnNwZHJfZWRnZXMgPC0KICBjY2xfcGZhbV9uc3BkciRjb3Jfd1t1cHBlci50cmkoY2NsX3BmYW1fbnNwZHIkY29yX3cpXQojIHAtdmFsdWUgPCAxZS00IG90aGVyd2lzZSBDQ0xhc3NvIG5ldHdvcmsgbnVtYmVyIG9mIGVkZ2VzIGlzIHZlcnkgbGFyZ2UKY2NsX3BmYW1fbnNwZHJfZWRnZXNbY2NsX3BmYW1fbnNwZHJfcHZhbHNfYWRqID4gMWUtNF0gPC0gMApjY2xfcGZhbV9uc3Bkcl9hbWF0IDwtCiAgbWF0cml4KDAsIGRpbShwZmFtX25zcGRyX2RhdGEpWzJdLCBkaW0ocGZhbV9uc3Bkcl9kYXRhKVsyXSkKcm93bmFtZXMoY2NsX3BmYW1fbnNwZHJfYW1hdCkgPC0gY29sbmFtZXMocGZhbV9uc3Bkcl9kYXRhKQpjb2xuYW1lcyhjY2xfcGZhbV9uc3Bkcl9hbWF0KSA8LSBjb2xuYW1lcyhwZmFtX25zcGRyX2RhdGEpCmNjbF9wZmFtX25zcGRyX2FtYXRbdXBwZXIudHJpKGNjbF9wZmFtX25zcGRyX2FtYXQpXSA8LSBjY2xfcGZhbV9uc3Bkcl9lZGdlcwpjY2xfcGZhbV9uc3Bkcl9nIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCgKICBjY2xfcGZhbV9uc3Bkcl9hbWF0LAogIG1vZGUgPSAidXBwZXIiLAogIHdlaWdodGVkID0gVFJVRSwKICBkaWFnID0gRkFMU0UKKQpjY2xfcGZhbV9uc3Bkcl9nIDwtIGluZHVjZWRfc3ViZ3JhcGgoCiAgY2NsX3BmYW1fbnNwZHJfZywKICBWKGNjbF9wZmFtX25zcGRyX2cpWwogICAgY29tcG9uZW50cyhjY2xfcGZhbV9uc3Bkcl9nKSRtZW1iZXJzaGlwID09CiAgICB3aGljaC5tYXgoY29tcG9uZW50cyhjY2xfcGZhbV9uc3Bkcl9nKSRjc2l6ZSkKICBdCikKY2NsX3BmYW1fbnNwZHJfZ192X21zayA8LSAoCiAgVihjY2xfcGZhbV9uc3Bkcl9nKSRuYW1lICVpbiUgVihjY2xfcGZhbV9uc3Bkcl9nKSRuYW1lCikKY2NsX3BmYW1fbnNwZHJfcF9nIDwtIGRlbGV0ZV9lZGdlcygKICBjY2xfcGZhbV9uc3Bkcl9nLAogIEUoY2NsX3BmYW1fbnNwZHJfZylbRShjY2xfcGZhbV9uc3Bkcl9nKSR3ZWlnaHQgPCAwXQopCmNjbF9wZmFtX25zcGRyX3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX25zcGRyX3BfZywKICBWKGNjbF9wZmFtX25zcGRyX3BfZylbCiAgICBjb21wb25lbnRzKGNjbF9wZmFtX25zcGRyX3BfZykkbWVtYmVyc2hpcCA9PQogICAgd2hpY2gubWF4KGNvbXBvbmVudHMoY2NsX3BmYW1fbnNwZHJfcF9nKSRjc2l6ZSkKICBdCikKY2NsX3BmYW1fbnNwZHJfcF9nX3ZfbXNrIDwtICgKICBWKGNjbF9wZmFtX25zcGRyX2cpJG5hbWUgJWluJSBWKGNjbF9wZmFtX25zcGRyX3BfZykkbmFtZQopCmNjbF9wZmFtX25zcGRyX25fZyA8LSBkZWxldGVfZWRnZXMoCiAgY2NsX3BmYW1fbnNwZHJfZywKICBFKGNjbF9wZmFtX25zcGRyX2cpW0UoY2NsX3BmYW1fbnNwZHJfZykkd2VpZ2h0ID4gMF0KKQpjY2xfcGZhbV9uc3Bkcl9uX2cgPC0gaW5kdWNlZF9zdWJncmFwaCgKICBjY2xfcGZhbV9uc3Bkcl9uX2csCiAgVihjY2xfcGZhbV9uc3Bkcl9uX2cpWwogICAgY29tcG9uZW50cyhjY2xfcGZhbV9uc3Bkcl9uX2cpJG1lbWJlcnNoaXAgPT0KICAgIHdoaWNoLm1heChjb21wb25lbnRzKGNjbF9wZmFtX25zcGRyX25fZykkY3NpemUpCiAgXQopCmNjbF9wZmFtX25zcGRyX25fZ192X21zayA8LSAoCiAgVihjY2xfcGZhbV9uc3Bkcl9nKSRuYW1lICVpbiUgVihjY2xfcGZhbV9uc3Bkcl9uX2cpJG5hbWUKKQpgYGAKCiMjIyBWaXN1YWxpemUgTmV0d29ya3MKCmBgYHtyLCBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD0xMH0KY2NsX3BmYW1fcnNwZHJfZ19jb29yZCA8LSBsYXlvdXRfd2l0aF9sZ2woY2NsX3BmYW1fcnNwZHJfZykKY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkIDwtCiAgY2NsX3BmYW1fcnNwZHJfZ19jb29yZFtjY2xfcGZhbV9yc3Bkcl9wX2dfdl9tc2ssICwgZHJvcCA9IEZdCmNjbF9wZmFtX3JzcGRyX25fZ19jb29yZCA8LQogIGNjbF9wZmFtX3JzcGRyX2dfY29vcmRbY2NsX3BmYW1fcnNwZHJfbl9nX3ZfbXNrLCAsIGRyb3AgPSBGXQpjY2xfcGZhbV9uc3Bkcl9nX2Nvb3JkIDwtIGxheW91dF93aXRoX2xnbChjY2xfcGZhbV9uc3Bkcl9nKQpjY2xfcGZhbV9uc3Bkcl9wX2dfY29vcmQgPC0KICBjY2xfcGZhbV9uc3Bkcl9nX2Nvb3JkW2NjbF9wZmFtX25zcGRyX3BfZ192X21zaywgLCBkcm9wID0gRl0KY2NsX3BmYW1fbnNwZHJfbl9nX2Nvb3JkIDwtCiAgY2NsX3BmYW1fbnNwZHJfZ19jb29yZFtjY2xfcGZhbV9uc3Bkcl9uX2dfdl9tc2ssICwgZHJvcCA9IEZdCnBhcihtZnJvdyA9IGMoMiwgMyksIG1hciA9IGMoMSwgMSwgMywgMSkpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9yc3Bkcl9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9yc3Bkcl9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICJQRkFNIFJlc3BvbmRlciBDQ0xhc3NvIE5ldHdvcmsiKQpwbG90X25ldHdvcmsoY2NsX3BmYW1fcnNwZHJfcF9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9yc3Bkcl9wX2dfY29vcmQsCiAgICAgICAgICAgICBtYWluID0gIisgSW50ZXJhY3Rpb25zIikKcGxvdF9uZXR3b3JrKGNjbF9wZmFtX3JzcGRyX25fZywKICAgICAgICAgICAgIGNvb3JkID0gY2NsX3BmYW1fcnNwZHJfbl9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICItIEludGVyYWN0aW9ucyIpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9uc3Bkcl9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9uc3Bkcl9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICJQRkFNIE5vbi1SZXNwb25kZXIgQ0NMYXNzbyBOZXR3b3JrIikKcGxvdF9uZXR3b3JrKGNjbF9wZmFtX25zcGRyX3BfZywKICAgICAgICAgICAgIGNvb3JkID0gY2NsX3BmYW1fbnNwZHJfcF9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICIrIEludGVyYWN0aW9ucyIpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9uc3Bkcl9uX2csCiAgICAgICAgICAgICBjb29yZCA9IGNjbF9wZmFtX25zcGRyX25fZ19jb29yZCwKICAgICAgICAgICAgIG1haW4gPSAiLSBJbnRlcmFjdGlvbnMiKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnBuZygKICAicmVzdWx0cy9QZmFtUm5SX0NDTGFzc29OZXQucG5nIiwKICBoZWlnaHQgPSA5LAogIHdpZHRoID0gMTAsCiAgdW5pdHMgPSAiaW4iLAogIHJlcyA9IDMwMAopCnBhcihtZnJvdyA9IGMoMiwgMyksIG1hciA9IGMoMSwgMSwgMywgMSkpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9yc3Bkcl9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9yc3Bkcl9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICJQRkFNIFJlc3BvbmRlciBDQ0xhc3NvIE5ldHdvcmsiKQpwbG90X25ldHdvcmsoY2NsX3BmYW1fcnNwZHJfcF9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9yc3Bkcl9wX2dfY29vcmQsCiAgICAgICAgICAgICBtYWluID0gIisgSW50ZXJhY3Rpb25zIikKcGxvdF9uZXR3b3JrKGNjbF9wZmFtX3JzcGRyX25fZywKICAgICAgICAgICAgIGNvb3JkID0gY2NsX3BmYW1fcnNwZHJfbl9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICItIEludGVyYWN0aW9ucyIpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9uc3Bkcl9nLAogICAgICAgICAgICAgY29vcmQgPSBjY2xfcGZhbV9uc3Bkcl9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICJQRkFNIE5vbi1SZXNwb25kZXIgQ0NMYXNzbyBOZXR3b3JrIikKcGxvdF9uZXR3b3JrKGNjbF9wZmFtX25zcGRyX3BfZywKICAgICAgICAgICAgIGNvb3JkID0gY2NsX3BmYW1fbnNwZHJfcF9nX2Nvb3JkLAogICAgICAgICAgICAgbWFpbiA9ICIrIEludGVyYWN0aW9ucyIpCnBsb3RfbmV0d29yayhjY2xfcGZhbV9uc3Bkcl9uX2csCiAgICAgICAgICAgICBjb29yZCA9IGNjbF9wZmFtX25zcGRyX25fZ19jb29yZCwKICAgICAgICAgICAgIG1haW4gPSAiLSBJbnRlcmFjdGlvbnMiKQppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCiMjIyBOZXR3b3JrIFN0YXRpc3RpY3MKCiMjIyMgRGVncmVlIGRpc3RyaWJ1dGlvbiwgc2hvcnRlc3QgcGF0aHMgZGlzdGFuY2UgZGlzdHJpYnV0aW9uLCB0cmFuc2l0aXZpdHk6CgpgYGB7ciwgZmlnLmhlaWdodD0xOCwgZmlnLndpZHRoPTEwfQpwYXIobWZyb3cgPSBjKDQsIDIpLCBtYXIgPSBjKDUuMSwgNC4xLCA0LjEsIDIuMSkpCiMgZGVncmVlIGRpc3RyaWJ1dGlvbgprX3IgPC0gZGVncmVlKGNjbF9wZmFtX3JzcGRyX2cpCmhpc3Qoa19yLAogICAgIGJyZWFrcyA9IDEwMCwKICAgICB4bGFiID0gImsiLAogICAgIHlsYWIgPSAiRnJlcXVlbmN5IiwKICAgICBtYWluID0gIlIgRGVncmVlIEZyZXF1ZW5jeSIpCmtfbiA8LSBkZWdyZWUoY2NsX3BmYW1fbnNwZHJfZykKaGlzdChrX24sCiAgICAgYnJlYWtzID0gMTAwLAogICAgIHhsYWIgPSAiayIsCiAgICAgeWxhYiA9IE5BLAogICAgIG1haW4gPSAiTlIgRGVncmVlIEZyZXF1ZW5jeSIpCmRkX3IgPC0gZGVncmVlX2Rpc3RyaWJ1dGlvbihjY2xfcGZhbV9yc3Bkcl9nKQpkZF9yX256ZXJvX3BvcyA8LSB3aGljaChkZF9yICE9IDApCnBrX3IgPC0gZGRfcltkZF9yX256ZXJvX3Bvc10KZHhfciA8LSAxOm1heChrX3IpCmR4X3IgPC0gZHhfcltkZF9yX256ZXJvX3Bvc10KcGxvdCgKICBwa19yIH4gbG9nKGR4X3IpLAogIGxvZyA9ICJ4eSIsCiAgeGxhYiA9ICJsb2cgayIsCiAgeWxhYiA9ICJsb2cgUGsiLAogIG1haW4gPSAiUiBMb2ctTG9nIERlZ3JlZSBEaXN0cmlidXRpb24iCikKZGRfbiA8LSBkZWdyZWVfZGlzdHJpYnV0aW9uKGNjbF9wZmFtX25zcGRyX2cpCmRkX25fbnplcm9fcG9zIDwtIHdoaWNoKGRkX24gIT0gMCkKcGtfbiA8LSBkZF9uW2RkX25fbnplcm9fcG9zXQpkeF9uIDwtIDE6bWF4KGtfbikKZHhfbiA8LSBkeF9uW2RkX25fbnplcm9fcG9zXQpwbG90KAogIHBrX24gfiBsb2coZHhfbiksCiAgbG9nID0gInh5IiwKICB4bGFiID0gImxvZyBrIiwKICB5bGFiID0gTkEsCiAgbWFpbiA9ICJOUiBMb2ctTG9nIERlZ3JlZSBEaXN0cmlidXRpb24iCikKIyBkaXN0YW5jZSBkaXN0cmlidXRpb24gb2Ygc2hvcnRlc3QgcGF0aHMKbnZfciA8LSB2Y291bnQoY2NsX3BmYW1fcnNwZHJfZykKYmZzX3JfdmVjIDwtIG1hdHJpeChucm93ID0gbnZfciwgbmNvbCA9IG52X3IpCmZvciAoaSBpbiAxOm52X3IpIHsKICBiZnNfcl92ZWNbaSwgXSA8LSBiZnMoCiAgICBjY2xfcGZhbV9yc3Bkcl9nLAogICAgcm9vdCA9IGksCiAgICBkaXN0ID0gVFJVRSwKICAgIHVucmVhY2hhYmxlID0gRkFMU0UKICApJGRpc3QKfQpkaXN0ZF9yIDwtIHRhYmxlKGJmc19yX3ZlYykgLyBzdW0oYmZzX3JfdmVjLCBuYS5ybSA9IFRSVUUpCmRpc3RkX3IgPC0gdGFpbChkaXN0ZF9yLCBsZW5ndGgoZGlzdGRfcikgLSAxKQpwbG90KAogIG5hbWVzKGRpc3RkX3IpLAogIGRpc3RkX3IsCiAgeGxhYiA9ICJEaXN0YW5jZSIsCiAgeWxhYiA9IGV4cHJlc3Npb24oUFtEaXN0YW5jZV0pLAogIG1haW4gPSAiUiBEaXN0YW5jZSBEaXN0cmlidXRpb24gb2YgU2hvcnRlc3QgUGF0aHMiCikKbnZfbiA8LSB2Y291bnQoY2NsX3BmYW1fbnNwZHJfZykKYmZzX25fdmVjIDwtIG1hdHJpeChucm93ID0gbnZfbiwgbmNvbCA9IG52X24pCmZvciAoaSBpbiAxOm52X24pIHsKICBiZnNfbl92ZWNbaSwgXSA8LSBiZnMoCiAgICBjY2xfcGZhbV9uc3Bkcl9nLAogICAgcm9vdCA9IGksCiAgICBkaXN0ID0gVFJVRSwKICAgIHVucmVhY2hhYmxlID0gRkFMU0UKICApJGRpc3QKfQpkaXN0ZF9uIDwtIHRhYmxlKGJmc19uX3ZlYykgLyBzdW0oYmZzX25fdmVjLCBuYS5ybSA9IFRSVUUpCmRpc3RkX24gPC0gdGFpbChkaXN0ZF9uLCBsZW5ndGgoZGlzdGRfbikgLSAxKQpwbG90KAogIG5hbWVzKGRpc3RkX24pLAogIGRpc3RkX24sCiAgeGxhYiA9ICJEaXN0YW5jZSIsCiAgeWxhYiA9IE5BLAogIG1haW4gPSAiTlIgRGlzdGFuY2UgRGlzdHJpYnV0aW9uIG9mIFNob3J0ZXN0IFBhdGhzIgopCiMgY2x1c3RlcmluZwpjbF9yIDwtIHRyYW5zaXRpdml0eShjY2xfcGZhbV9yc3Bkcl9nLCB0eXBlID0gImxvY2FsIikKY2xfcl9kZiA8LSBkYXRhLmZyYW1lKGNsdXN0ID0gY2xfciwgZGVncmVlID0ga19yKSAlPiUKICBncm91cF9ieShkZWdyZWUpICU+JQogIHN1bW1hcml6ZShtY2x1c3QgPSBtZWFuKGNsdXN0LCBuYS5ybSA9IFRSVUUpKQpwbG90KAogIG1jbHVzdCB+IGRlZ3JlZSwKICBkYXRhID0gY2xfcl9kZiwKICB4bGFiID0gImsiLAogIHlsYWIgPSAiQyhrKSIsCiAgbWFpbiA9ICJSIENsdXN0ZXJpbmcgQ29lZmZpY2VudCIKKQpjbF9uIDwtIHRyYW5zaXRpdml0eShjY2xfcGZhbV9uc3Bkcl9nLCB0eXBlID0gImxvY2FsIikKY2xfbl9kZiA8LSBkYXRhLmZyYW1lKGNsdXN0ID0gY2xfbiwgZGVncmVlID0ga19uKSAlPiUKICBncm91cF9ieShkZWdyZWUpICU+JQogIHN1bW1hcml6ZShtY2x1c3QgPSBtZWFuKGNsdXN0LCBuYS5ybSA9IFRSVUUpKQpwbG90KAogIG1jbHVzdCB+IGRlZ3JlZSwKICBkYXRhID0gY2xfbl9kZiwKICB4bGFiID0gImsiLAogIHlsYWIgPSBOQSwKICBtYWluID0gIk5SIENsdXN0ZXJpbmcgQ29lZmZpY2VudCIKKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnBuZygKICAicmVzdWx0cy9QZmFtUm5SX0NDTGFzc29OZXRfU3RhdHMucG5nIiwKICBoZWlnaHQgPSAxOCwKICB3aWR0aCA9IDEwLAogIHVuaXRzID0gImluIiwKICByZXMgPSAzMDAKKQpwYXIobWZyb3cgPSBjKDQsIDIpLCBtYXIgPSBjKDUuMSwgNC4xLCA0LjEsIDIuMSkpCmhpc3Qoa19yLAogICAgIGJyZWFrcyA9IDEwMCwKICAgICB4bGFiID0gImsiLAogICAgIHlsYWIgPSAiRnJlcXVlbmN5IiwKICAgICBtYWluID0gIlIgRGVncmVlIEZyZXF1ZW5jeSIpCmhpc3Qoa19uLAogICAgIGJyZWFrcyA9IDEwMCwKICAgICB4bGFiID0gImsiLAogICAgIHlsYWIgPSBOQSwKICAgICBtYWluID0gIk5SIERlZ3JlZSBGcmVxdWVuY3kiKQpwbG90KAogIHBrX3IgfiBsb2coZHhfciksCiAgbG9nID0gInh5IiwKICB4bGFiID0gImxvZyBrIiwKICB5bGFiID0gImxvZyBQayIsCiAgbWFpbiA9ICJSIExvZy1Mb2cgRGVncmVlIERpc3QiCikKcGxvdCgKICBwa19uIH4gbG9nKGR4X24pLAogIGxvZyA9ICJ4eSIsCiAgeGxhYiA9ICJsb2cgayIsCiAgeWxhYiA9IE5BLAogIG1haW4gPSAiTlIgTG9nLUxvZyBEZWdyZWUgRGlzdCIKKQpwbG90KAogIG5hbWVzKGRpc3RkX3IpLAogIGRpc3RkX3IsCiAgeGxhYiA9ICJEaXN0YW5jZSIsCiAgeWxhYiA9IGV4cHJlc3Npb24oUFtEaXN0YW5jZV0pLAogIG1haW4gPSAiUiBEaXN0YW5jZSBEaXN0cmlidXRpb24gb2YgU2hvcnRlc3QgUGF0aHMiCikKcGxvdCgKICBuYW1lcyhkaXN0ZF9uKSwKICBkaXN0ZF9uLAogIHhsYWIgPSAiRGlzdGFuY2UiLAogIHlsYWIgPSBOQSwKICBtYWluID0gIk5SIERpc3RhbmNlIERpc3RyaWJ1dGlvbiBvZiBTaG9ydGVzdCBQYXRocyIKKQpwbG90KAogIG1jbHVzdCB+IGRlZ3JlZSwKICBkYXRhID0gY2xfcl9kZiwKICB4bGFiID0gImsiLAogIHlsYWIgPSAiQyhrKSIsCiAgbWFpbiA9ICJSIENsdXN0ZXJpbmcgQ29lZmZpY2VudCIKKQpwbG90KAogIG1jbHVzdCB+IGRlZ3JlZSwKICBkYXRhID0gY2xfbl9kZiwKICB4bGFiID0gImsiLAogIHlsYWIgPSBOQSwKICBtYWluID0gIk5SIENsdXN0ZXJpbmcgQ29lZmZpY2VudCIKKQppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCiMjIyMgQWRkaXRpb25hbCBzdGF0aXN0aWNzOgoKKipOb3RlOiBjb3VsZCBub3QgY2FsY3VsYXRlIGJldHdlZW5uZXNzIGFuZCBjbG9zZW5lc3Mgb24gbmV0d29ya3Mgc2luY2UgaXQgYXBwZWFycwpuZXR3b3JrcyBhcmUgdG9vIGxhcmdlIGFuZCBpdCBjcmFzaGVzIHRoZSBSIHNlc3Npb24qKgoKYGBge3J9CmRhdGEuZnJhbWUoCiAgIkNsdXN0ZXJpbmcgY29lZi4iID0gYygKICAgIHRyYW5zaXRpdml0eShjY2xfcGZhbV9yc3Bkcl9nLCB0eXBlID0gImdsb2JhbCIpLAogICAgdHJhbnNpdGl2aXR5KGNjbF9wZmFtX25zcGRyX2csIHR5cGUgPSAiZ2xvYmFsIikKICApLAogICJQb3dlciBsYXcgY29lZi4iID0gYygKICAgIGZpdF9wb3dlcl9sYXcoa19yLCB4bWluID0gMSkkYWxwaGEsCiAgICBmaXRfcG93ZXJfbGF3KGtfbiwgeG1pbiA9IDEpJGFscGhhCiAgKSwKICAiTWVhbiBzaG9ydGVzdCBwYXRoIiA9IGMoCiAgICBtZWFuKGJmc19yX3ZlYywgbmEucm0gPSBUUlVFKSwKICAgIG1lYW4oYmZzX25fdmVjLCBuYS5ybSA9IFRSVUUpCiAgKSwKICAiRGVuc2l0eSIgPSBjKAogICAgZWRnZV9kZW5zaXR5KGNjbF9wZmFtX3JzcGRyX2cpLAogICAgZWRnZV9kZW5zaXR5KGNjbF9wZmFtX25zcGRyX2cpCiAgKSwKICByb3cubmFtZXMgPSBjKCJSZXNwb25kZXJzIiwgIk5vbi1SZXNwb25kZXJzIikKKQpgYGAKCiMjIyBDb21tdW5pdHkgRGV0ZWN0aW9uIGluIFJlc3BvbmRlci9Ob24tUmVzcG9uZGVyIE5ldHdvcmtzIFVzaW5nIEluZm9NYXAgYW5kIFNwaW5HbGFzcwoKYGBge3IsIGZpZy5oZWlnaHQ9MjcsIGZpZy53aWR0aD0xMH0KbnVtX3RvcF9jbHVzdHMgPC0gMwoKIyBSZXNwb25kZXIgK05ldHdvcmsgSW5mb01hcApjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcCA8LSBjbHVzdGVyX2luZm9tYXAoY2NsX3BmYW1fcnNwZHJfcF9nKQpjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3BfaWRzIDwtIHNvcnQoYXMubnVtZXJpYyhuYW1lcygKICBzb3J0KHNpemVzKGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6bnVtX3RvcF9jbHVzdHNdCikpKQpjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3Bfdl9tc2sgPC0gKAogIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwJG1lbWJlcnNoaXAgJWluJSBjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3BfaWRzCikKY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wX2cgPC0gaW5kdWNlZF9zdWJncmFwaCgKICBjY2xfcGZhbV9yc3Bkcl9wX2csCiAgVihjY2xfcGZhbV9yc3Bkcl9wX2cpW2NjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF92X21za10KKQojIGlHcmFwaCBoYXMgbm8gZnVuY3Rpb25hbGl0eSB0byBzdWJzZXQgY29tbXVuaXR5IG9iamVjdHMgc28gaGFjayBpdApjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3AgPC0gY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXAKY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wJG5hbWVzIDwtCiAgY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wJG5hbWVzW2NjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF92X21za10KY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wJG1lbWJlcnNoaXAgPC0KICBjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3AkbWVtYmVyc2hpcFtjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3Bfdl9tc2tdCmNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcCR2Y291bnQgPC0gbGVuZ3RoKGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcCRuYW1lcykKCiMgUmVzcG9uZGVyICtOZXR3b3JrIFNwaW5HbGFzcwojIFNwaW5HbGFzcyB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fZmlsZSA8LSAiZGF0YS9jY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbi5SZGEiCmlmICghZmlsZS5leGlzdHMoY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fZmlsZSkpIHsKICBjY2xfcGZhbV9yc3Bkcl9wX2dfc3BpbiA8LSBjbHVzdGVyX3NwaW5nbGFzcyhjY2xfcGZhbV9yc3Bkcl9wX2cpCiAgc2F2ZShjY2xfcGZhbV9yc3Bkcl9wX2dfc3BpbiwgZmlsZSA9IGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX2ZpbGUpCn0gZWxzZSB7CiAgbG9hZChjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl9maWxlKQp9CmNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF9pZHMgPC0gc29ydChhcy5udW1lcmljKG5hbWVzKAogIHNvcnQoc2l6ZXMoY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4pLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpudW1fdG9wX2NsdXN0c10KKSkpCmNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF92X21zayA8LSAoCiAgY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4kbWVtYmVyc2hpcCAlaW4lIGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF9pZHMKKQpjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl90b3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX3JzcGRyX3BfZywKICBWKGNjbF9wZmFtX3JzcGRyX3BfZylbY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wX3ZfbXNrXQopCiMgaUdyYXBoIGhhcyBubyBmdW5jdGlvbmFsaXR5IHRvIHN1YnNldCBjb21tdW5pdHkgb2JqZWN0cyBzbyBoYWNrIGl0CmNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcCA8LSBjY2xfcGZhbV9yc3Bkcl9wX2dfc3BpbgpjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl90b3AkbmFtZXMgPC0KICBjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl90b3AkbmFtZXNbY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wX3ZfbXNrXQpjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl90b3AkbWVtYmVyc2hpcCA8LQogIGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcCRtZW1iZXJzaGlwW2NjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF92X21za10KY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wJHZjb3VudCA8LSBsZW5ndGgoY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wJG5hbWVzKQoKIyBSZXNwb25kZXIgKy8tTmV0d29yayBTcGluR2xhc3MKIyBTcGluR2xhc3MgdGFrZXMgYSBsb25nIHRpbWUgdG8gcnVuIChsb2FkIGNhY2hlZCBpZiBhdmFpbGFibGUpCmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl9maWxlIDwtICJkYXRhL2NjbF9wZmFtX3JzcGRyX2dfc3Bpbi5SZGEiCmlmICghZmlsZS5leGlzdHMoY2NsX3BmYW1fcnNwZHJfZ19zcGluX2ZpbGUpKSB7CiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluIDwtIGNsdXN0ZXJfc3BpbmdsYXNzKGNjbF9wZmFtX3JzcGRyX2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltcGxlbWVudGF0aW9uID0gIm5lZyIpCiAgc2F2ZShjY2xfcGZhbV9yc3Bkcl9nX3NwaW4sIGZpbGUgPSBjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fZmlsZSkKfSBlbHNlIHsKICBsb2FkKGNjbF9wZmFtX3JzcGRyX2dfc3Bpbl9maWxlKQp9CmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3BfaWRzIDwtIHNvcnQoYXMubnVtZXJpYyhuYW1lcygKICBzb3J0KHNpemVzKGNjbF9wZmFtX3JzcGRyX2dfc3BpbiksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOm51bV90b3BfY2x1c3RzXQopKSkKY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF92X21zayA8LSAoCiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluJG1lbWJlcnNoaXAgJWluJSBjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wX2lkcwopCmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX3JzcGRyX2csCiAgVihjY2xfcGZhbV9yc3Bkcl9nKVtjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wX3ZfbXNrXQopCiMgaUdyYXBoIGhhcyBubyBmdW5jdGlvbmFsaXR5IHRvIHN1YnNldCBjb21tdW5pdHkgb2JqZWN0cyBzbyBoYWNrIGl0CmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3AgPC0gY2NsX3BmYW1fcnNwZHJfZ19zcGluCmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3AkbmFtZXMgPC0KICBjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wJG5hbWVzW2NjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3Bfdl9tc2tdCmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3AkbWVtYmVyc2hpcCA8LQogIGNjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3AkbWVtYmVyc2hpcFtjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wX3ZfbXNrXQpjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wJHZjb3VudCA8LSBsZW5ndGgoY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcCRuYW1lcykKCiMgTm9uLVJlc3BvbmRlciArTmV0d29yayBJbmZvTWFwCmNjbF9wZmFtX25zcGRyX3BfZ19pbWFwIDwtIGNsdXN0ZXJfaW5mb21hcChjY2xfcGZhbV9uc3Bkcl9wX2cpCmNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcF9pZHMgPC0gc29ydChhcy5udW1lcmljKG5hbWVzKAogIHNvcnQoc2l6ZXMoY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXApLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpudW1fdG9wX2NsdXN0c10KKSkpCmNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcF92X21zayA8LSAoCiAgY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXAkbWVtYmVyc2hpcCAlaW4lIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcF9pZHMKKQpjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX25zcGRyX3BfZywKICBWKGNjbF9wZmFtX25zcGRyX3BfZylbY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfdG9wX3ZfbXNrXQopCiMgaUdyYXBoIGhhcyBubyBmdW5jdGlvbmFsaXR5IHRvIHN1YnNldCBjb21tdW5pdHkgb2JqZWN0cyBzbyBoYWNrIGl0CmNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcCA8LSBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcApjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3AkbmFtZXMgPC0KICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3AkbmFtZXNbY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfdG9wX3ZfbXNrXQpjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3AkbWVtYmVyc2hpcCA8LQogIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcCRtZW1iZXJzaGlwW2NjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcF92X21za10KY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfdG9wJHZjb3VudCA8LSBsZW5ndGgoY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfdG9wJG5hbWVzKQoKIyBOb24tUmVzcG9uZGVyICtOZXR3b3JrIFNwaW5HbGFzcwojIFNwaW5HbGFzcyB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4gKGxvYWQgY2FjaGVkIGlmIGF2YWlsYWJsZSkKY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fZmlsZSA8LSAiZGF0YS9jY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbi5SZGEiCmlmICghZmlsZS5leGlzdHMoY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fZmlsZSkpIHsKICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbiA8LSBjbHVzdGVyX3NwaW5nbGFzcyhjY2xfcGZhbV9uc3Bkcl9wX2cpCiAgc2F2ZShjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbiwgZmlsZSA9IGNjbF9wZmFtX25zcGRyX3BfZ19zcGluX2ZpbGUpCn0gZWxzZSB7CiAgbG9hZChjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl9maWxlKQp9CmNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcF9pZHMgPC0gc29ydChhcy5udW1lcmljKG5hbWVzKAogIHNvcnQoc2l6ZXMoY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW4pLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpudW1fdG9wX2NsdXN0c10KKSkpCmNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcF92X21zayA8LSAoCiAgY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW4kbWVtYmVyc2hpcCAlaW4lIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcF9pZHMKKQpjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3BfZyA8LSBpbmR1Y2VkX3N1YmdyYXBoKAogIGNjbF9wZmFtX25zcGRyX3BfZywKICBWKGNjbF9wZmFtX25zcGRyX3BfZylbY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fdG9wX3ZfbXNrXQopCiMgaUdyYXBoIGhhcyBubyBmdW5jdGlvbmFsaXR5IHRvIHN1YnNldCBjb21tdW5pdHkgb2JqZWN0cyBzbyBoYWNrIGl0CmNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcCA8LSBjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbgpjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3AkbmFtZXMgPC0KICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3AkbmFtZXNbY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fdG9wX3ZfbXNrXQpjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3AkbWVtYmVyc2hpcCA8LQogIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcCRtZW1iZXJzaGlwW2NjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcF92X21za10KY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fdG9wJHZjb3VudCA8LSBsZW5ndGgoY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fdG9wJG5hbWVzKQoKIyBOb24tUmVzcG9uZGVyICsvLU5ldHdvcmsgU3BpbkdsYXNzCiMgU3BpbkdsYXNzIHRha2VzIGEgbG9uZyB0aW1lIHRvIHJ1biAobG9hZCBjYWNoZWQgaWYgYXZhaWxhYmxlKQpjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fZmlsZSA8LSAiZGF0YS9jY2xfcGZhbV9uc3Bkcl9nX3NwaW4uUmRhIgppZiAoIWZpbGUuZXhpc3RzKGNjbF9wZmFtX25zcGRyX2dfc3Bpbl9maWxlKSkgewogIGNjbF9wZmFtX25zcGRyX2dfc3BpbiA8LSBjbHVzdGVyX3NwaW5nbGFzcyhjY2xfcGZhbV9uc3Bkcl9nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbXBsZW1lbnRhdGlvbiA9ICJuZWciKQogIHNhdmUoY2NsX3BmYW1fbnNwZHJfZ19zcGluLCBmaWxlID0gY2NsX3BmYW1fbnNwZHJfZ19zcGluX2ZpbGUpCn0gZWxzZSB7CiAgbG9hZChjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fZmlsZSkKfQpjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wX2lkcyA8LSBzb3J0KGFzLm51bWVyaWMobmFtZXMoCiAgc29ydChzaXplcyhjY2xfcGZhbV9uc3Bkcl9nX3NwaW4pLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpudW1fdG9wX2NsdXN0c10KKSkpCmNjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3Bfdl9tc2sgPC0gKAogIGNjbF9wZmFtX25zcGRyX2dfc3BpbiRtZW1iZXJzaGlwICVpbiUgY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcF9pZHMKKQpjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wX2cgPC0gaW5kdWNlZF9zdWJncmFwaCgKICBjY2xfcGZhbV9uc3Bkcl9nLAogIFYoY2NsX3BmYW1fbnNwZHJfZylbY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcF92X21za10KKQojIGlHcmFwaCBoYXMgbm8gZnVuY3Rpb25hbGl0eSB0byBzdWJzZXQgY29tbXVuaXR5IG9iamVjdHMgc28gaGFjayBpdApjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wIDwtIGNjbF9wZmFtX25zcGRyX2dfc3BpbgpjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wJG5hbWVzIDwtCiAgY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcCRuYW1lc1tjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wX3ZfbXNrXQpjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wJG1lbWJlcnNoaXAgPC0KICBjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wJG1lbWJlcnNoaXBbY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcF92X21za10KY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcCR2Y291bnQgPC0gbGVuZ3RoKGNjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3AkbmFtZXMpCgpwYXIobWZyb3cgPSBjKDYsIDIpLCBtYXIgPSBjKDEsIDEsIDMsIDEpKQpwbG90KAogIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwLAogIGNjbF9wZmFtX3JzcGRyX3BfZywKICBsYXlvdXQgPSBjY2xfcGZhbV9yc3Bkcl9wX2dfY29vcmQsCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSAiUEZBTSBSZXNwb25kZXIgQ0NMYXNzbyArTmV0d29yayBJbmZvTWFwIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wLAogIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXApW2NjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkW2NjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwCiAgKSksIGFscGhhID0gMSlbY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wX2lkc10sCiAgbWFyay5jb2wgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwCiAgKSksIGFscGhhID0gMC4zKVtjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3BfaWRzXSwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9IHBhc3RlKCJUb3AiLCBudW1fdG9wX2NsdXN0cywgIkNsdXN0ZXJzIikKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwLAogIGNjbF9wZmFtX25zcGRyX3BfZywKICBsYXlvdXQgPSBjY2xfcGZhbV9uc3Bkcl9wX2dfY29vcmQsCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSAiUEZBTSBOb24tUmVzcG9uZGVyIENDTGFzc28gK05ldHdvcmsgSW5mb01hcCBDbHVzdGVycyIKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcCwKICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3BfZywKICBjb2wgPSBtZW1iZXJzaGlwKGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwKVtjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3Bfdl9tc2tdLAogIGxheW91dCA9IGNjbF9wZmFtX25zcGRyX3BfZ19jb29yZFtjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3Bfdl9tc2ssICwgZHJvcCA9IEZdLAogIG1hcmsuYm9yZGVyID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcAogICkpLCBhbHBoYSA9IDEpW2NjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcAogICkpLCBhbHBoYSA9IDAuMylbY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfdG9wX2lkc10sCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSBwYXN0ZSgiVG9wIiwgbnVtX3RvcF9jbHVzdHMsICJDbHVzdGVycyIpCikKcGxvdCgKICBjY2xfcGZhbV9yc3Bkcl9wX2dfc3BpbiwKICBjY2xfcGZhbV9yc3Bkcl9wX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gIlBGQU0gUmVzcG9uZGVyIENDTGFzc28gK05ldHdvcmsgU3BpbkdsYXNzIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wLAogIGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4pW2NjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkW2NjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluCiAgKSksCiAgYWxwaGEgPSAxKVtjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl90b3BfaWRzXSwKICBtYXJrLmNvbCA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKAogICAgY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4KICApKSwKICBhbHBoYSA9IDAuMylbY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wX2lkc10sCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSBwYXN0ZSgiVG9wIiwgbnVtX3RvcF9jbHVzdHMsICJDbHVzdGVycyIpCikKcGxvdCgKICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbiwKICBjY2xfcGZhbV9uc3Bkcl9wX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fbnNwZHJfcF9nX2Nvb3JkLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gIlBGQU0gTm9uLVJlc3BvbmRlciBDQ0xhc3NvICtOZXR3b3JrIFNwaW5HbGFzcyBDbHVzdGVycyIKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcCwKICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3BfZywKICBjb2wgPSBtZW1iZXJzaGlwKGNjbF9wZmFtX25zcGRyX3BfZ19zcGluKVtjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3Bfdl9tc2tdLAogIGxheW91dCA9IGNjbF9wZmFtX25zcGRyX3BfZ19jb29yZFtjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3Bfdl9tc2ssICwgZHJvcCA9IEZdLAogIG1hcmsuYm9yZGVyID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbgogICkpLAogIGFscGhhID0gMSlbY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fdG9wX2lkc10sCiAgbWFyay5jb2wgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluCiAgKSksCiAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluLAogIGNjbF9wZmFtX3JzcGRyX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIFJlc3BvbmRlciBDQ0xhc3NvICsvLU5ldHdvcmsgU3BpbkdsYXNzIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcCwKICBjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wX2csCiAgY29sID0gbWVtYmVyc2hpcChjY2xfcGZhbV9yc3Bkcl9nX3NwaW4pW2NjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3Bfdl9tc2tdLAogIGxheW91dCA9IGNjbF9wZmFtX3JzcGRyX2dfY29vcmRbY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX2dfc3BpbgogICkpLAogIGFscGhhID0gMSlbY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9yc3Bkcl9nX3NwaW4KICApKSwKICBhbHBoYSA9IDAuMylbY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fbnNwZHJfZ19zcGluLAogIGNjbF9wZmFtX25zcGRyX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fbnNwZHJfZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIE5vbi1SZXNwb25kZXIgQ0NMYXNzbyArLy1OZXR3b3JrIFNwaW5HbGFzcyBDbHVzdGVycyIKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3AsCiAgY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fbnNwZHJfZ19zcGluKVtjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wX3ZfbXNrXSwKICBsYXlvdXQgPSBjY2xfcGZhbV9uc3Bkcl9nX2Nvb3JkW2NjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3Bfdl9tc2ssICwgZHJvcCA9IEZdLAogIG1hcmsuYm9yZGVyID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9nX3NwaW4KICApKSwKICBhbHBoYSA9IDEpW2NjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3BfaWRzXSwKICBtYXJrLmNvbCA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKAogICAgY2NsX3BmYW1fbnNwZHJfZ19zcGluCiAgKSksCiAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3BfaWRzXSwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9IHBhc3RlKCJUb3AiLCBudW1fdG9wX2NsdXN0cywgIkNsdXN0ZXJzIikKKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnBuZygKICAicmVzdWx0cy9QZmFtUm5SX0NDTGFzc29OZXRfQ2x1c3RlcnMucG5nIiwKICBoZWlnaHQgPSAyNywKICB3aWR0aCA9IDEwLAogIHVuaXRzID0gImluIiwKICByZXMgPSAzMDAKKQpwYXIobWZyb3cgPSBjKDYsIDIpLCBtYXIgPSBjKDEsIDEsIDMsIDEpKQpwbG90KAogIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwLAogIGNjbF9wZmFtX3JzcGRyX3BfZywKICBsYXlvdXQgPSBjY2xfcGZhbV9yc3Bkcl9wX2dfY29vcmQsCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSAiUEZBTSBSZXNwb25kZXIgQ0NMYXNzbyArTmV0d29yayBJbmZvTWFwIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wLAogIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXApW2NjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkW2NjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwCiAgKSksIGFscGhhID0gMSlbY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfdG9wX2lkc10sCiAgbWFyay5jb2wgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwCiAgKSksIGFscGhhID0gMC4zKVtjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF90b3BfaWRzXSwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9IHBhc3RlKCJUb3AiLCBudW1fdG9wX2NsdXN0cywgIkNsdXN0ZXJzIikKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwLAogIGNjbF9wZmFtX25zcGRyX3BfZywKICBsYXlvdXQgPSBjY2xfcGZhbV9uc3Bkcl9wX2dfY29vcmQsCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSAiUEZBTSBOb24tUmVzcG9uZGVyIENDTGFzc28gK05ldHdvcmsgSW5mb01hcCBDbHVzdGVycyIKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcCwKICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3BfZywKICBjb2wgPSBtZW1iZXJzaGlwKGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwKVtjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3Bfdl9tc2tdLAogIGxheW91dCA9IGNjbF9wZmFtX25zcGRyX3BfZ19jb29yZFtjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF90b3Bfdl9tc2ssICwgZHJvcCA9IEZdLAogIG1hcmsuYm9yZGVyID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcAogICkpLCBhbHBoYSA9IDEpW2NjbF9wZmFtX25zcGRyX3BfZ19pbWFwX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcAogICkpLCBhbHBoYSA9IDAuMylbY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfdG9wX2lkc10sCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSBwYXN0ZSgiVG9wIiwgbnVtX3RvcF9jbHVzdHMsICJDbHVzdGVycyIpCikKcGxvdCgKICBjY2xfcGZhbV9yc3Bkcl9wX2dfc3BpbiwKICBjY2xfcGZhbV9yc3Bkcl9wX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gIlBGQU0gUmVzcG9uZGVyIENDTGFzc28gK05ldHdvcmsgU3BpbkdsYXNzIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wLAogIGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4pW2NjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF92X21za10sCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfcF9nX2Nvb3JkW2NjbF9wZmFtX3JzcGRyX3BfZ19zcGluX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluCiAgKSksCiAgYWxwaGEgPSAxKVtjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl90b3BfaWRzXSwKICBtYXJrLmNvbCA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKAogICAgY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4KICApKSwKICBhbHBoYSA9IDAuMylbY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fdG9wX2lkc10sCiAgdmVydGV4LnNpemUgPSA0LAogIHZlcnRleC5sYWJlbC5jZXggPSAwLjI1LAogIG1haW4gPSBwYXN0ZSgiVG9wIiwgbnVtX3RvcF9jbHVzdHMsICJDbHVzdGVycyIpCikKcGxvdCgKICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbiwKICBjY2xfcGZhbV9uc3Bkcl9wX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fbnNwZHJfcF9nX2Nvb3JkLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gIlBGQU0gTm9uLVJlc3BvbmRlciBDQ0xhc3NvICtOZXR3b3JrIFNwaW5HbGFzcyBDbHVzdGVycyIKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcCwKICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3BfZywKICBjb2wgPSBtZW1iZXJzaGlwKGNjbF9wZmFtX25zcGRyX3BfZ19zcGluKVtjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3Bfdl9tc2tdLAogIGxheW91dCA9IGNjbF9wZmFtX25zcGRyX3BfZ19jb29yZFtjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl90b3Bfdl9tc2ssICwgZHJvcCA9IEZdLAogIG1hcmsuYm9yZGVyID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9wX2dfc3BpbgogICkpLAogIGFscGhhID0gMSlbY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fdG9wX2lkc10sCiAgbWFyay5jb2wgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluCiAgKSksCiAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX25zcGRyX3BfZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluLAogIGNjbF9wZmFtX3JzcGRyX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fcnNwZHJfZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIFJlc3BvbmRlciBDQ0xhc3NvICsvLU5ldHdvcmsgU3BpbkdsYXNzIENsdXN0ZXJzIgopCnBsb3QoCiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcCwKICBjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fdG9wX2csCiAgY29sID0gbWVtYmVyc2hpcChjY2xfcGZhbV9yc3Bkcl9nX3NwaW4pW2NjbF9wZmFtX3JzcGRyX2dfc3Bpbl90b3Bfdl9tc2tdLAogIGxheW91dCA9IGNjbF9wZmFtX3JzcGRyX2dfY29vcmRbY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF92X21zaywgLCBkcm9wID0gRl0sCiAgbWFyay5ib3JkZXIgPSByYWluYm93KGxlbmd0aChjb21tdW5pdGllcygKICAgIGNjbF9wZmFtX3JzcGRyX2dfc3BpbgogICkpLAogIGFscGhhID0gMSlbY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF9pZHNdLAogIG1hcmsuY29sID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9yc3Bkcl9nX3NwaW4KICApKSwKICBhbHBoYSA9IDAuMylbY2NsX3BmYW1fcnNwZHJfZ19zcGluX3RvcF9pZHNdLAogIHZlcnRleC5zaXplID0gNCwKICB2ZXJ0ZXgubGFiZWwuY2V4ID0gMC4yNSwKICBtYWluID0gcGFzdGUoIlRvcCIsIG51bV90b3BfY2x1c3RzLCAiQ2x1c3RlcnMiKQopCnBsb3QoCiAgY2NsX3BmYW1fbnNwZHJfZ19zcGluLAogIGNjbF9wZmFtX25zcGRyX2csCiAgbGF5b3V0ID0gY2NsX3BmYW1fbnNwZHJfZ19jb29yZCwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9ICJQRkFNIE5vbi1SZXNwb25kZXIgQ0NMYXNzbyArLy1OZXR3b3JrIFNwaW5HbGFzcyBDbHVzdGVycyIKKQpwbG90KAogIGNjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3AsCiAgY2NsX3BmYW1fbnNwZHJfZ19zcGluX3RvcF9nLAogIGNvbCA9IG1lbWJlcnNoaXAoY2NsX3BmYW1fbnNwZHJfZ19zcGluKVtjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fdG9wX3ZfbXNrXSwKICBsYXlvdXQgPSBjY2xfcGZhbV9uc3Bkcl9nX2Nvb3JkW2NjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3Bfdl9tc2ssICwgZHJvcCA9IEZdLAogIG1hcmsuYm9yZGVyID0gcmFpbmJvdyhsZW5ndGgoY29tbXVuaXRpZXMoCiAgICBjY2xfcGZhbV9uc3Bkcl9nX3NwaW4KICApKSwKICBhbHBoYSA9IDEpW2NjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3BfaWRzXSwKICBtYXJrLmNvbCA9IHJhaW5ib3cobGVuZ3RoKGNvbW11bml0aWVzKAogICAgY2NsX3BmYW1fbnNwZHJfZ19zcGluCiAgKSksCiAgYWxwaGEgPSAwLjMpW2NjbF9wZmFtX25zcGRyX2dfc3Bpbl90b3BfaWRzXSwKICB2ZXJ0ZXguc2l6ZSA9IDQsCiAgdmVydGV4LmxhYmVsLmNleCA9IDAuMjUsCiAgbWFpbiA9IHBhc3RlKCJUb3AiLCBudW1fdG9wX2NsdXN0cywgIkNsdXN0ZXJzIikKKQppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCiMjIyBTYXZlIGNsdXN0ZXIgcmVzdWx0czoKCmBgYHtyfQojIFJlc3BvbmRlcgpjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXAkbWVtYmVyc2hpcCkpIHsKICBjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF9kYXRhIDwtIHJiaW5kKAogICAgY2NsX3BmYW1fcnNwZHJfcF9nX2ltYXBfZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3JzcGRyX3BfZykkbmFtZVsKICAgICAgICBjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcCRtZW1iZXJzaGlwID09IGkKICAgICAgXSwKICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICApCiAgKQp9CmNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3JzcGRyX3BfZ19pbWFwX2RhdGEsIHBmYW1fZmVhdCwga2V5ID0gIkFjY2Vzc2lvbiIpCndyaXRlLmNzdihjY2xfcGZhbV9yc3Bkcl9wX2dfaW1hcF9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1ScHJfQ0NMYXNzb1Bvc05ldF9JbmZvTWFwQ2x1c3RlcnMuY3N2IiwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQpjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW4kbWVtYmVyc2hpcCkpIHsKICBjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl9kYXRhIDwtIHJiaW5kKAogICAgY2NsX3BmYW1fcnNwZHJfcF9nX3NwaW5fZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3JzcGRyX3BfZykkbmFtZVsKICAgICAgICBjY2xfcGZhbV9yc3Bkcl9wX2dfc3BpbiRtZW1iZXJzaGlwID09IGkKICAgICAgXSwKICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICApCiAgKQp9CmNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3JzcGRyX3BfZ19zcGluX2RhdGEsIHBmYW1fZmVhdCwga2V5ID0gIkFjY2Vzc2lvbiIpCndyaXRlLmNzdihjY2xfcGZhbV9yc3Bkcl9wX2dfc3Bpbl9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1ScHJfQ0NMYXNzb1Bvc05ldF9TcGluR2xhc3NDbHVzdGVycy5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmNjbF9wZmFtX3JzcGRyX2dfc3Bpbl9kYXRhIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTptYXgoY2NsX3BmYW1fcnNwZHJfZ19zcGluJG1lbWJlcnNoaXApKSB7CiAgY2NsX3BmYW1fcnNwZHJfZ19zcGluX2RhdGEgPC0gcmJpbmQoCiAgICBjY2xfcGZhbV9yc3Bkcl9nX3NwaW5fZGF0YSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIENsdXN0ZXIgPSBpLAogICAgICBBY2Nlc3Npb24gPSBWKGNjbF9wZmFtX3JzcGRyX2cpJG5hbWVbCiAgICAgICAgY2NsX3BmYW1fcnNwZHJfZ19zcGluJG1lbWJlcnNoaXAgPT0gaQogICAgICBdLAogICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICkKICApCn0KY2NsX3BmYW1fcnNwZHJfZ19zcGluX2RhdGEgPC0KICBtZXJnZS5lYXN5KGNjbF9wZmFtX3JzcGRyX2dfc3Bpbl9kYXRhLCBwZmFtX2ZlYXQsIGtleSA9ICJBY2Nlc3Npb24iKQp3cml0ZS5jc3YoY2NsX3BmYW1fcnNwZHJfZ19zcGluX2RhdGEsCiAgICAgICAgICBmaWxlID0gInJlc3VsdHMvUGZhbVJwcl9DQ0xhc3NvTmV0X1NwaW5HbGFzc0NsdXN0ZXJzLmNzdiIpCgojIE5vbi1SZXNwb25kZXIKY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfZGF0YSA8LSBkYXRhLmZyYW1lKCkKZm9yIChpIGluIDE6bWF4KGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwJG1lbWJlcnNoaXApKSB7CiAgY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfZGF0YSA8LSByYmluZCgKICAgIGNjbF9wZmFtX25zcGRyX3BfZ19pbWFwX2RhdGEsCiAgICBkYXRhLmZyYW1lKAogICAgICBDbHVzdGVyID0gaSwKICAgICAgQWNjZXNzaW9uID0gVihjY2xfcGZhbV9uc3Bkcl9wX2cpJG5hbWVbCiAgICAgICAgY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXAkbWVtYmVyc2hpcCA9PSBpCiAgICAgIF0sCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgKQogICkKfQpjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF9kYXRhIDwtCiAgbWVyZ2UuZWFzeShjY2xfcGZhbV9uc3Bkcl9wX2dfaW1hcF9kYXRhLCBwZmFtX2ZlYXQsIGtleSA9ICJBY2Nlc3Npb24iKQp3cml0ZS5jc3YoY2NsX3BmYW1fbnNwZHJfcF9nX2ltYXBfZGF0YSwKICAgICAgICAgIGZpbGUgPSAicmVzdWx0cy9QZmFtTnByX0NDTGFzc29Qb3NOZXRfSW5mb01hcENsdXN0ZXJzLmNzdiIsCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fZGF0YSA8LSBkYXRhLmZyYW1lKCkKZm9yIChpIGluIDE6bWF4KGNjbF9wZmFtX25zcGRyX3BfZ19zcGluJG1lbWJlcnNoaXApKSB7CiAgY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fZGF0YSA8LSByYmluZCgKICAgIGNjbF9wZmFtX25zcGRyX3BfZ19zcGluX2RhdGEsCiAgICBkYXRhLmZyYW1lKAogICAgICBDbHVzdGVyID0gaSwKICAgICAgQWNjZXNzaW9uID0gVihjY2xfcGZhbV9uc3Bkcl9wX2cpJG5hbWVbCiAgICAgICAgY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW4kbWVtYmVyc2hpcCA9PSBpCiAgICAgIF0sCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgKQogICkKfQpjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl9kYXRhIDwtCiAgbWVyZ2UuZWFzeShjY2xfcGZhbV9uc3Bkcl9wX2dfc3Bpbl9kYXRhLCBwZmFtX2ZlYXQsIGtleSA9ICJBY2Nlc3Npb24iKQp3cml0ZS5jc3YoY2NsX3BmYW1fbnNwZHJfcF9nX3NwaW5fZGF0YSwKICAgICAgICAgIGZpbGUgPSAicmVzdWx0cy9QZmFtTnByX0NDTGFzc29Qb3NOZXRfU3BpbkdsYXNzQ2x1c3RlcnMuY3N2IiwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQpjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fZGF0YSA8LSBkYXRhLmZyYW1lKCkKZm9yIChpIGluIDE6bWF4KGNjbF9wZmFtX25zcGRyX2dfc3BpbiRtZW1iZXJzaGlwKSkgewogIGNjbF9wZmFtX25zcGRyX2dfc3Bpbl9kYXRhIDwtIHJiaW5kKAogICAgY2NsX3BmYW1fbnNwZHJfZ19zcGluX2RhdGEsCiAgICBkYXRhLmZyYW1lKAogICAgICBDbHVzdGVyID0gaSwKICAgICAgQWNjZXNzaW9uID0gVihjY2xfcGZhbV9uc3Bkcl9nKSRuYW1lWwogICAgICAgIGNjbF9wZmFtX25zcGRyX2dfc3BpbiRtZW1iZXJzaGlwID09IGkKICAgICAgXSwKICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICApCiAgKQp9CmNjbF9wZmFtX25zcGRyX2dfc3Bpbl9kYXRhIDwtCiAgbWVyZ2UuZWFzeShjY2xfcGZhbV9uc3Bkcl9nX3NwaW5fZGF0YSwgcGZhbV9mZWF0LCBrZXkgPSAiQWNjZXNzaW9uIikKd3JpdGUuY3N2KGNjbF9wZmFtX25zcGRyX2dfc3Bpbl9kYXRhLAogICAgICAgICAgZmlsZSA9ICJyZXN1bHRzL1BmYW1OcHJfQ0NMYXNzb05ldF9TcGluR2xhc3NDbHVzdGVycy5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAo=